import {
    BroadcastRequest,
    BroadcastResponse,
    BroadcastResponseStatus,
    BroadcastType,
    Controller,
    ControllerNo,
    GuiId,
    TimeControllerOpt
} from "./model/ControllerOpt";
import {
    AttrPercentMap,
    Attrs,
    BAllStr,
    CaluAttrStr,
    CaluType,
    ChangeUnitType,
    ColorStr,
    CombinationInfo,
    CombinationUIInfo,
    CombitionRet,
    MainAttr,
    MaterialInfo,
    OrderStr,
    PathCache,
    SpecialEffectType,
    VestJson
} from "../type/Type";
import {Util} from "../utils/Util";
import {GlobalEnv} from "../utils/GlobalEnv";
import {ControllerBroadcast} from "./ControllerBroadcast";
import {
    AchievementSerializeBody,
    SerializeBody,
    SerializeLoadType,
    UnitLoadType,
    UnitSerializeBody
} from "./model/SerializeBody";
import {
    AchievementBeneficiaryType,
    AffixData,
    AttributeEnum,
    GenerateData,
    GenerateDataType,
    LocType,
    PathData,
    PathType,
    Personality,
    PlayerAttribute,
    TransData,
    UnitAttribute,
    UnitData,
    UnitPoolData,
    UnitShopType,
    UnitType,
    WeightIDData
} from "../frame/dataSource/DataModel";
import {DataRouter} from "../frame/dataSource/DataRouter";
import {ConfigKey, ExpAttenuationSetting, PetCallType, PetResumeType} from "../constant/ConfigConstant";
import {DataRouterUtil} from "../utils/router/DataRouterUtil";
import Configer from "../frame/apiModel/config/Configer";
import {FinalKey} from "../constant/FinalConstant";
import {Logger} from "../frame/apiModel/config/Logger";
import {UnitModel} from "../frame/apiModel/adapterModel/model3/UnitModel";
import {PlayerFactory} from "../frame/PlayerFactory";
import {PlayerModel} from "../frame/apiModel/adapterModel/model4/PlayerModel";
import {LocModel} from "../frame/apiModel/base/LocModel";
import {EnterType, TriggerFactory} from "../frame/TriggerFactory";
import {PlayerDataKey, UnitActionLevel, UnitDataKey} from "../frame/dataSource/DataSource";
import {ItemJson, UnitJson} from "../frame/apiModel/model/StoreJson";
import {CodeUtil} from "../frame/codeAdapter/CodeUtil";
import {ModelFactory} from "../frame/apiModel/adapterModel/model5/ModelFactory";
import {TimeController} from "./TimeController";
import {ItemModel} from "../frame/apiModel/adapterModel/model3/ItemModel";
import {TexttagModel} from "../frame/apiModel/adapterModel/model2/TexttagModel";
import UnitUtil from "../utils/UnitUtil";
import {RectModel} from "../frame/apiModel/adapterModel/model2/RectModel";
import {GlobalModel} from "../frame/apiModel/adapterModel/model1/GlobalModel";
import {ResourceKey} from "../constant/ResourceConstant";
import {ModelType} from "../frame/apiModel/adapter/adapterInterface/model5/IModelFactory";
import MathUtil from "../frame/codeAdapter/MathUtil";

// 一些附加在单位上的功能，比如单位的自定义属性
export class UnitController implements Controller {
    getControllerNo(): number {
        return ControllerNo.UnitController;
    }
    static get unitItem_generateData_Map(): Map<UnitModel | ItemModel, GenerateData> {
        return this._unitItem_generateData_Map;
    }

    private static unitController: UnitController = new UnitController();
    // 野怪单位与monsterData映射
    private static _unitItem_generateData_Map: Map<UnitModel | ItemModel, GenerateData> = new Map<UnitModel | ItemModel, GenerateData>();
    //
    // 属性计算队列
    private static caluTaskMap: Map<string, string> = new Map<string, string>();
    // 单位行为记录
    private static pathRegist: Map<string, Map<UnitModel, PathCache>> = new Map<string, Map<UnitModel, PathCache>>();

    private static forbiddenRegist: Map<string, Map<string, { text: string, fun: (text: string, unit: UnitModel, player: PlayerModel) => void }>> = new Map<string, Map<string, { text: string, fun: (text: string, unit: UnitModel, player: PlayerModel) => void }>>();

    private static hatredRegist: { [key: string]: boolean } = {};


    static getInstance(): UnitController {
        return UnitController.unitController;
    }


    init = () => {
        ControllerBroadcast.getInstance().startListen(UnitController.getInstance());

        const allMap = Configer.getFinal(FinalKey.allMapRect);

       // TriggerFactory.getInstance().registerUnitEnterRect(allMap, UnitController.getInstance().addDamageFun)

        const resetAllUnit = Configer.getConfig(ConfigKey.resetAllUnit);
        if (!CodeUtil.isNullOrZero(resetAllUnit)) {
            const units = ModelFactory.getInstance().initRectUnit();
            for (let i = 0; i < units.length; i++) {
                const unit = units[i];
                this.initUnit(unit);
            }
        }
    }
    checkUnitPool(player: PlayerModel, data: UnitPoolData, achs: { [key: string]: number }, mapLevel: number, maxDifficult: number) {
        const id = data.id;
        let bFree = true;
        if (!CodeUtil.isNullOrZero(data.buy)) {
            bFree = false;
            const bHave = player.getHasMallItem(id);
            if (bHave) {
                return true
            }
        }
        if (!CodeUtil.isNullOrZero(data.map_level)) {
            bFree = false;
            if (data.map_level > mapLevel) {
                return true
            }
        }
        if (!CodeUtil.isNullOrZero(data.difficult)) {
            bFree = false;
            if (!CodeUtil.isNullOrZero(maxDifficult)) {
                if (maxDifficult >= data.difficult) {
                    return true;
                }
            }
        }
        if (data.achs) {
            bFree = false;
            const achId = data.achs;
            const achNum = achs && achs[achId]
            if (!CodeUtil.isNullOrZero(achNum)) {
                return true;
            }
        }
        return bFree;
    }
    getInitUnitArrs(player: PlayerModel, difficult: number, achs: { [key: string]: number }) {
        if (player?.isValid()) {
        } else {
            return [];
        }
        const utilIdRArr: UnitPoolData[] = [];
        const utilPoolRDatas = DataRouter.getUtilPoolDatas();

        const mapLevel = player.getMapLevel();
        let maxDifficult = 0;
        if (!CodeUtil.isNullOrZero(difficult)) {
            maxDifficult = difficult;
        }
        for (let i = 0; i < utilPoolRDatas.length; i++) {
            const data = JSON.parse(JSON.stringify(utilPoolRDatas[i]));
            const ret = this.checkUnitPool(player, data, achs, mapLevel, maxDifficult);
            if (ret) {
                data.enable = ret;
            }
            utilIdRArr.push(data);
        }
        utilIdRArr.sort((a, b) => {
            if (a.enable || b.enable) {
                if (b.enable) {
                    return 1;
                } else if (a.enable) {
                    return -1;
                }
            }
            return  0
        })
        player.data[PlayerDataKey.initUnitPoolData] = utilIdRArr;

        return utilIdRArr;
    }

    loadByJson(json: UnitSerializeBody, player: PlayerModel) {
        const startPos = player.startLocation;

        const archiveIndex = player.data[PlayerDataKey.archiveIndex];


        this.mapUnitSerialize(archiveIndex, SerializeLoadType.current_global, (key: string, index: number, type: UnitLoadType) => {
            const value: UnitJson = json[key];
            if (!CodeUtil.isNullOrZero(value?.id)) {
            } else {
                return;
            }
            switch (type) {
                case UnitLoadType.hi: {
                    const heroGroup = UnitController.getInstance().createUnit(1, value, player, startPos, 0, true);
                    break;
                }
                case UnitLoadType.bi: {
                    const boxGroup = UnitController.getInstance().createUnit(1, value, player, startPos, 0);
                    GlobalEnv.setBox(player, boxGroup[0])
                    break;
                }
                case UnitLoadType.pi: {
                    if (!CodeUtil.isNullOrZero(value?.id)) {
                        this.addPet(player, value, true);
                    }
                    break;
                }
            }
        })
    }

    createBox(id: string, whichPlayer: PlayerModel, loc: LocModel, lookAt: any) {
        const bagJson: UnitJson = { id: id, e: 0 }
        const bagPetgroup = UnitController.getInstance().createUnit(1, bagJson, whichPlayer, loc, 0);
        Util.mapUnitArr(bagPetgroup,
            (unit) => {
                GlobalEnv.setBox(whichPlayer, unit);
            }
        )
        return bagPetgroup;
    }
    initUnit(unit: UnitModel) {
        const diffData = GlobalEnv.getCurrentDiffData();

        if (!CodeUtil.isNullOrZero(diffData.autoCounterattack)) {
            this.toEnemyTeam(unit);
        }

        const whichPlayer = PlayerFactory.getInstance().getUnitOwner(unit);

        const newestAttributes = unit.data[UnitDataKey.newestAttribute];
        if (newestAttributes) {
            return;
        }
        let change;
        if (whichPlayer?.isNotEndPlaying()) {
            change = this.addAttributeUnit(unit, false)
        } else {
            change = this.addAttributeUnit(unit, true);


        }

        if (change) {
            this.addCaluAttributeTaskByUnit(unit, false,'初始化')
        }
    }
    createUnit(count: number, unitJson: UnitJson, whichPlayer: PlayerModel, loc: LocModel, lookAt: number, bHero: boolean = false, targetPlayer?: PlayerModel) {
        if (unitJson && unitJson.id) {
        } else {
            Logger.toastError(`单位Id为空，无法创建`);
            return;
        }
        // Logger.toastError(`<${unitJson.id}>`)
        const arr: UnitModel[] = [];
        for (let i = 0; i < count; i ++) {
            const unit = TriggerFactory.getInstance().createUnitModel(whichPlayer.playerId, unitJson, loc, lookAt);
            if (unit?.isValid()) {
                this.initUnit(unit);
                if (bHero) {
                    this.addHero(unit);
                    const request: BroadcastRequest = { broadcastType: BroadcastType.applyAutoTask, controllerNo: ControllerNo.TaskController, data: { player: whichPlayer } }
                    ControllerBroadcast.getInstance().broadcast(request);

                    const str = DataRouterUtil.getUnitStrByUnitId(unitJson.id);
                    const handBookData = DataRouter.getHandBookDataByUnitId(str);
                    if (handBookData) {
                        const handBookId = handBookData.id;
                        const handBookUnitInfo: UnitJson = { id: handBookId, e: 1 }
                        const request: BroadcastRequest = { broadcastType: BroadcastType.initHandBook, controllerNo: ControllerNo.AchievementController, data: { handBookUnitInfo: handBookUnitInfo, player: whichPlayer } }
                        ControllerBroadcast.getInstance().broadcast(request);
                    }
                }
                arr.push(unit);
                if (targetPlayer) {
                    this.addPlayerUnitMap(targetPlayer, unit);
                }

                const unitId = unit.getHandleId();
                const unitData = DataRouter.getUnitDataByUnitId(unitId)
                const hatredRange = unitData?.hatredRange;
                if (!CodeUtil.isNullOrZero(hatredRange)) {
                    UnitController.hatredRegist[unitId] = true;
                }

            }
        }
        return arr;
    }
    killUnit(killer: UnitModel, killed: UnitModel) {
        if (killer) {
            const player = PlayerFactory.getInstance().getUnitOwner(killer);
            if (player?.isNotEndPlaying()) {
                this.rewardKiller(killer, killed);
                const unitId = DataRouterUtil.getUnitStr(killed);
                const killRequest: BroadcastRequest = { broadcastType: BroadcastType.recordKillUnitNum, controllerNo: ControllerNo.TaskController, data: { player, unitId: unitId } }
                ControllerBroadcast.getInstance().broadcast(killRequest);
            }
        }

        const callBack = () => {
            this.unitDeadRemove(killed);
        }
        const clearUnitTime: number = Configer.getConfig(ConfigKey.clearUnitTime)
        const timerOpt = new TimeControllerOpt(undefined, callBack, 1);
        TimeController.getInstance().startTimer({ time: clearUnitTime, opt: timerOpt });
    }
    toEnemyTeam(unit: UnitModel) {
        const unitData = unit?.template;
        if (unitData) {
            const bCounterattack = unitData.counterattack;
            if (!CodeUtil.isNullOrZero(bCounterattack)) {
                const type = unitData.personality;
                switch (type) {
                    case Personality.active: {
                        const enemyTeam = Configer.getConfig(ConfigKey.enemyTeam) as number;
                        const enemyPlayer = GlobalEnv.getTeamFreePlayer(enemyTeam);
                        if (enemyPlayer?.isValid()) {
                            enemyPlayer.toBeUnitOwner(unit, true);
                        }
                        UnitController.getInstance().addEnemyUnit(unit);
                        break;
                    }
                    default: {
                        const neutralHostilityId = Configer.getConfig(ConfigKey.neutralHostilityId)
                        const neutralHostilityPlayer = PlayerFactory.getInstance().getPlayer(neutralHostilityId);
                        neutralHostilityPlayer.toBeUnitOwner(unit, true);
                        break;
                    }
                }
            }
        }
    }
    // 单位死亡后  如果没有其他需求就在系统中删除掉这个单位
    // 这个方法中所有的方法移除前都需要判断一下是否拥有
    itemDeadRemove(item: ItemModel) {
        this.clearGenerateData(item);
    }
    unitDeadRemove(deadUnit: UnitModel) {
        if (deadUnit?.isValid()) {
        } else {
            return;
        }
        const deadPlayer = PlayerFactory.getInstance().getUnitOwner(deadUnit)
        const playerCheck = deadPlayer?.isNotEndPlaying();
        if (playerCheck) {
            const ret = this.checkClear(deadUnit);
            if (ret) {
                this.clearUnitByUnit(deadUnit);
            }
        } else {
            this.clearUnitByUnit(deadUnit);
        }
    }
    isInPets (unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        const pets = player?.data[PlayerDataKey.pet];
        const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
        if (pets) {
        } else {
            return false
        }
        for (let i = 0; i < maxPetNum; i ++) {
            const pet = pets[i];
            if (pet === unit) {
                return true
            }
        }
        return false;
    }
    checkClear(unit: UnitModel) {
        const isHero = GlobalEnv.isInHeroGroup(unit);
        const isPet = this.isInPets(unit);
        if (isHero) {
            return false
        }
        if (isPet) {
            return false
        }
        return true
    }
    clearGenerateData(unit: UnitModel | ItemModel) {
        if (UnitController.unitItem_generateData_Map.has(unit)) {
            UnitController.unitItem_generateData_Map.delete(unit);
        }
        PlayerFactory.getInstance().mapAllPlayer((player) => {
            const unitMap = player.data[PlayerDataKey.generateUnitMap];
            const id = GlobalModel.getHandleId(unit);
            if (id && unitMap[id]) {
                unitMap[id] = undefined
            }
        })
    }

    getPlayerAttr(player: PlayerModel) {
        if (player?.isValid()) {
            const attr = player.data[PlayerDataKey.newestAttrs];
            if (attr) {
                return attr;
            }
        }
        return new PlayerAttribute();
    }
    rewardKiller = (killer: UnitModel, killed: UnitModel) => {
        const strId = killed?.strId;
        const unitData = DataRouter.getUnitDataByUnitId(strId);
        const player = PlayerFactory.getInstance().getUnitOwner(killer)
        if (unitData && player) {
            const bountyplus = unitData.bountyplus;
            const ret = UnitUtil.getAttribute(killer);
            const attribute = ret.newestAttribute;
            if (!CodeUtil.isNullOrZero(bountyplus)) {
                const coinAddRadix = Util.getTargetAttributeEnumByHero(AttributeEnum.coinAddRadix, attribute, killer);
                const rewardCoin = bountyplus * ( 1 + coinAddRadix / 100);
                UnitUtil.addCoin(killer, player, rewardCoin);
            }
            const lumberbountyplus = unitData.lumberbountyplus;
            if (!CodeUtil.isNullOrZero(lumberbountyplus)) {
                const woodAddRadix = Util.getTargetAttributeEnumByHero(AttributeEnum.woodAddRadix, attribute, killer);
                const rewardWood = lumberbountyplus * ( 1 + woodAddRadix / 100);
                UnitUtil.addWood(killer, player, rewardWood);
            }
            const expAddValue = unitData.expAddValue;
            if (!CodeUtil.isNullOrZero(expAddValue)) {
                // 以被击杀单位半径内单位增加经验
                const killedLoc = killed.loc;
                const range: number = Configer.getConfig(ConfigKey.expRange);

                const arr: UnitModel[] = [];
                const killedPlayer = PlayerFactory.getInstance().getUnitOwner(killed);
                ModelFactory.getInstance().mapUnitInRange(killedLoc, range,
                    (unit) => {
                        const tempPlayer = PlayerFactory.getInstance().getUnitOwner(unit);
                        let checkCamp = !!killedPlayer && !killedPlayer.isPlayerAlly(tempPlayer);
                        const isInBattle = unit.isHero() && unit.isAliveInBattle();
                        if(checkCamp && isInBattle) {
                            arr.push(unit);
                        }
                    }
                )
                const len = arr.length;
                const newExpAddValue = this.getExpAttenuationByNum(expAddValue, len);
                for (let i = 0; i < len; i++) {
                    const enumUnit = arr[i];
                    const ret = UnitUtil.getAttribute(enumUnit);
                    const attribute = ret.newestAttribute;
                    if (attribute) {
                    } else {
                        return;
                    }
                    let tempExpAddValue = newExpAddValue;
                    const targetLv = enumUnit.level;
                    const sourceLv = killed.level;
                    const subLv = MathUtil.abs(targetLv - sourceLv);

                    tempExpAddValue = this.getExpAttenuationByLv(tempExpAddValue, subLv);

                    const expAddRadix = Util.getTargetAttributeEnumByHero(AttributeEnum.expAddRadix, attribute, enumUnit);
                    const exp = enumUnit.experience;
                    let rewardExp = tempExpAddValue * expAddRadix / 100;
                    const newExp = exp + tempExpAddValue + rewardExp;
                    Util.setTargetAttributeEnumToNotHero(enumUnit, AttributeEnum.exp, newExp);
                }
            }
        }
    }
    getExpAttenuationByLv(exp: number, subLv: number): number {
        const expAttenuation: ExpAttenuationSetting = Configer.getConfig(ConfigKey.expAttenuation);
        const keys = Object.keys(expAttenuation);
        for (let i = 0; i < keys.length; i++) {
            const key: number = MathUtil.toNumber(keys[i]);
            const value = expAttenuation[key];
            if (subLv > key) {
                return  value
            }
        }
        return exp;
    }
    getExpAttenuationByNum(exp: number, size: number) {
        let attenuation = Configer.getConfig(ConfigKey.attenuation);
        const maxAttenuation = Configer.getConfig(ConfigKey.maxAttenuation);

        attenuation = attenuation * (size - 1);

        if (attenuation > maxAttenuation) {
            attenuation = maxAttenuation;
        }

        let newExp = exp * (1 - (attenuation / 100));
        return newExp
    }
    getHeroLvCache(unit: UnitModel) {
        const cache = unit.data[UnitDataKey.maxLvCache];
        if (cache) {
            return cache;
        }
        return 0;
    }
    setHeroLvCache(unit: UnitModel, newNum: number) {
        unit.data[UnitDataKey.maxLvCache] = newNum;
    }

    reflushShareBoss(onlyReflush: boolean = true) {
        const bossArr = GlobalEnv.shareBossArr;
        const len = bossArr.length;
        const maxLife = GlobalEnv.shareBossMaxLife;
        const life = GlobalEnv.shareBossLife;
        if (life && life > 0) {
            for (let i = 0; i < len; i++) {
                const boss = bossArr[i];
                boss.maxLife = maxLife;
                boss.life = life
            }
        } else {
            if (onlyReflush) {
            } else {
                for (let i = 0; i < len; i++) {
                    const boss = bossArr[i];
                    if (boss?.isAlive()) {
                        const request: BroadcastRequest = { broadcastType: BroadcastType.dropFun, controllerNo: ControllerNo.TaskController, data: { killed: boss } }
                        ControllerBroadcast.getInstance().broadcast(request);
                        boss.kill();
                    }
                }
            }
        }
    }
    addShareBoss(unit: UnitModel) {
        GlobalEnv.shareBossArr.push(unit);

        let allMaxLife = GlobalEnv.shareBossMaxLife;
        let currentLife = GlobalEnv.shareBossLife;

        allMaxLife = allMaxLife + unit.maxLife;
        currentLife = currentLife + unit.life;

        GlobalEnv.shareBossLife = currentLife;
        GlobalEnv.shareBossMaxLife = allMaxLife;

        this.reflushShareBoss();
    }
    removeShareBoss(unit: UnitModel, bExit: boolean) {
        if (unit?.isValid()) {
            const shareBossArr = GlobalEnv.shareBossArr;
            const index = shareBossArr.indexOf(unit);
            if (index >= 0) {
                shareBossArr.splice(index, 1);
                if (bExit) {
                } else {
                    let allMaxLife = GlobalEnv.shareBossMaxLife;
                    let currentLife = GlobalEnv.shareBossLife;

                    // const life = unit.life > 0 ? unit.life : 0;
                    // currentLife = currentLife - life;

                    const maxLife = unit.maxLife > 0 ? unit.maxLife : 0;
                    allMaxLife = allMaxLife - maxLife;

                    if (allMaxLife > 0) {
                        GlobalEnv.shareBossMaxLife = allMaxLife;
                    } else {
                        GlobalEnv.shareBossMaxLife = 0;
                    }

                    if (currentLife > 0) {
                        GlobalEnv.shareBossLife = currentLife;
                    } else {
                        GlobalEnv.shareBossLife = 0;
                    }

                    this.reflushShareBoss();
                }
            }
        }
    }
    // 两种情况

    getAttrByUnitData(unitData: UnitData) {
        // Util.loggerInDev(`物品数量:${num}`, LogType.goodDev)
        return  DataRouterUtil.getAttrByAttrInterface(unitData?.attrId, 1);
    }

    isTimelyAttribute(attrs: Attrs) {
        const keys: AttributeEnum[] = [
            AttributeEnum.agiP,
            AttributeEnum.agile,
            AttributeEnum.strP,
            AttributeEnum.strength,
            AttributeEnum.intP,
            AttributeEnum.intellect,
            // AttributeEnum.attackP,
            // AttributeEnum.attack,
            AttributeEnum.speed,
            AttributeEnum.attackSpeed,
            AttributeEnum.maxLifeP,
            AttributeEnum.maxLife,
            AttributeEnum.maxMagicP,
            AttributeEnum.maxMagic,
            AttributeEnum.attackDistance,
            AttributeEnum.size,
            AttributeEnum.attackSpeedInterval
        ]
        let change = false;
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            const unitAttr = attrs.unitAttr;
            // const playerAttr = attrs.playerAttr;
            let value = unitAttr[key];
            if (!CodeUtil.isNullOrZero(value)) {
                change = true;
                break;
            }
            // value = playerAttr[key];
            // if (value && value > 0) {
            //     change = true;
            //     break;
            // }
        }
        return change;
    }

    initAttribute(unit: UnitModel, useAffix: boolean) {
        const unitData = DataRouter.getUnitDataByUnitId(unit?.strId);

        let attrs = this.getAttrByUnitData(unitData);

        let change = this.isTimelyAttribute(attrs);

        if (useAffix) {
            const tempChange = this.addAffixByDiff(unit);
            if (tempChange) {
                const affixAttr = this.getAffixAttr(unit);
                attrs = Util.sumAttributes(attrs, affixAttr, CaluType.add);
                change = true;
            }

            const currentDifficult = GlobalEnv.getCurrentDiffData();
            const currentPlayNum = PlayerFactory.getInstance().getPlayingPlayerNum();
            const waveAttackStrengthen = unitData?.waveAttackStrengthen ? unitData?.waveAttackStrengthen : 0;
            const waveLifeStrengthen = unitData?.waveLifeStrengthen ? unitData?.waveLifeStrengthen : 0;
            const playerNumAddRadixAttack = unitData?.playerNumAddRadixAttack ? unitData?.playerNumAddRadixAttack : '';
            const playerNumAddRadixLife = unitData?.playerNumAddRadixLife ? unitData?.playerNumAddRadixLife : '';
            const wave = GlobalEnv.wave;
            DataRouterUtil.setMonsterAttrByWaveAndDifficuteAndPlayerNum(attrs, wave, currentDifficult, currentPlayNum, waveAttackStrengthen, waveLifeStrengthen, playerNumAddRadixAttack, playerNumAddRadixLife);

            this.initShopItem(unit);
        }

        const request: BroadcastRequest = { broadcastType: BroadcastType.initAbilityAttr, controllerNo: ControllerNo.AbilityController, data: { unit: unit } }
        ControllerBroadcast.getInstance().broadcast(request);

        this.setInitAttributes(unit, attrs);
        return change
    }

    initShopItem(unit: UnitModel) {
        const items = unit.template?.Sellitems;
        const itemArr = CodeUtil.getArrBySplit(items);
        for (let i = 0; i < itemArr.length; i++) {
            const itemId = itemArr[i];
            if (!CodeUtil.isNullOrZero(itemId)) {
                PlayerFactory.getInstance().mapAllNotEndPlayingPlayer((player: PlayerModel) => {
                    const itemShopInfo = player.data[PlayerDataKey.itemShopMap][itemId];
                    if (itemShopInfo) {
                    } else {
                        const itemData = DataRouter.getItemDataByItemId(itemId);
                        const cl = itemData?.stockStart ? itemData.stockStart : 0;
                        const stockInit = itemData?.stockInit ? itemData.stockInit : 0;
                        player.data[PlayerDataKey.itemShopMap][itemId] = { id: itemId, num: stockInit, cl, type: UnitShopType.normal };
                    }
                })
            }
        }
    }

    caluAttribute(unit: UnitModel) {
        // 计算前的属性值
        const ret = UnitUtil.getAttribute(unit);
        let newestAttrLog = ret.newestAttribute
        if (newestAttrLog) {
        } else {
            // DataRouterUtil.loggerUnitName(unit, '未找到目标单位属性');
            return;
        }
        Logger.toastAttr(`----------------------------------`)
        const enmuAttr = Configer.getConfig(ConfigKey.trackAttr) as string;
        // 更新最新属性在这里
        Logger.toastAttr(`当前计算属性的单位：${unit?.name}`)
        Logger.toastAttr(`当前监听属性:${AttributeEnum[enmuAttr]}`)
        Logger.toastAttr(`属性增益系统`)
        const extraAttr = ret.extraAttr;
        Logger.toastAttr(`单位上一次增加的属性: ${extraAttr[enmuAttr]}`)

        let newAddAttrs = this.getNewAddAttribute(unit);
        Logger.toastAttr(`新增加的属性: ${newAddAttrs[enmuAttr]}`)

        const cosumeAttr = ret.subExtraAttr;
        Logger.toastAttr(`单位的消耗属性: ${cosumeAttr[enmuAttr]}`)

        newAddAttrs = Util.sumUnitAttribute(newAddAttrs, cosumeAttr, CaluType.add);

        let originAttr = ret.originAttr;
        // let whiteAttr = ret.whiteAttr;
        Logger.toastAttr(`单位的原值: ${originAttr[enmuAttr]}`)


        // 类似最大生命值这种属性需要用 原值 加上 新加的属性 来计算出最新的属性
        let newestAttr = Util.sumUnitAttribute(originAttr, newAddAttrs, CaluType.add);
        Logger.toastAttr(`加上新增加的属性: ${newestAttr[enmuAttr]}`)

        // 将 主属性  计算成真正的 属性值
        this.caluMainAttr(unit, newAddAttrs);
        Logger.toastAttr(`计算主属性后的属性: ${newAddAttrs[enmuAttr]}`)

        // 将百分比属性变成真正的属性值, 真正的属性会转换到newAddAttrs里面
        this.caluAttrP(unit, newestAttr, newAddAttrs);
        Logger.toastAttr(`计算百分比主属性后的属性: ${newAddAttrs[enmuAttr]}`)

        // 将 百分比主属性 计算成真正的属性值
        this.caluMainPAttr(unit, newestAttr, newAddAttrs)
        Logger.toastAttr(`计算百分比加成后增加的属性: ${newAddAttrs[enmuAttr]}`)

        // 将计算百分比之后的属性更新到 最新属性中
        newestAttr = Util.sumUnitAttribute(originAttr, newAddAttrs, CaluType.add);
        Logger.toastAttr(`计算百分比加成后的属性: ${newestAttr[enmuAttr]}`)

        // 根据新计算的属性设置单位各个属性
        // todo 性能 如果需要节省性能可以把这几个值取整
        // 这几个绿字属性需要从最新添加的属性中获取

        // 把属性值刷新到单位身上
        this.reflushAttrToUnit(unit, newAddAttrs, extraAttr, newestAttr);

        unit.data[UnitDataKey.newestAttribute] = newestAttr;
        unit.data[UnitDataKey.extraAttribute] = newAddAttrs;
        Logger.toastAttr(`----------------------------------`)
        return newAddAttrs
    }
    caluMainPAttr(unit: UnitModel, newestAttr: UnitAttribute, newAddAttrs: UnitAttribute) {
        const mainAttrValue = newAddAttrs[AttributeEnum.mainAttrP];
        if (!CodeUtil.isNullOrZero(mainAttrValue)) {
            const unitId = unit?.strId;
            const unitData = DataRouter.getUnitDataByUnitId(unitId);
            if (unitData) {
                const mainAttr = unitData?.Primary;
                switch (mainAttr) {
                    case MainAttr.AGI:{
                        newAddAttrs[AttributeEnum.agile] = newAddAttrs[AttributeEnum.agile] + newestAttr[AttributeEnum.agile] * (mainAttrValue / 100);
                        break;
                    }
                    case MainAttr.STR:{
                        newAddAttrs[AttributeEnum.strength] = newAddAttrs[AttributeEnum.strength] + newestAttr[AttributeEnum.strength] * (mainAttrValue / 100);
                        break;
                    }
                    case MainAttr.INT:{
                        newAddAttrs[AttributeEnum.intellect] = newAddAttrs[AttributeEnum.intellect] + newestAttr[AttributeEnum.intellect] * (mainAttrValue / 100);
                        break;
                    }
                }
            }
        }
    }
    caluMainAttr(unit: UnitModel, newAddAttrs: UnitAttribute) {
        const mainAttrValue = newAddAttrs[AttributeEnum.mainAttr];
        if (!CodeUtil.isNullOrZero(mainAttrValue)) {
            const unitId = unit?.strId;
            const unitData = DataRouter.getUnitDataByUnitId(unitId);
            if (unitData) {
                const mainAttr = unitData?.Primary;
                switch (mainAttr) {
                    case MainAttr.AGI:{
                        newAddAttrs[AttributeEnum.agile] = newAddAttrs[AttributeEnum.agile] + mainAttrValue;
                        break;
                    }
                    case MainAttr.STR:{
                        newAddAttrs[AttributeEnum.strength] = newAddAttrs[AttributeEnum.strength] + mainAttrValue;
                        break;
                    }
                    case MainAttr.INT:{
                        newAddAttrs[AttributeEnum.intellect] = newAddAttrs[AttributeEnum.intellect] + mainAttrValue;
                        break;
                    }
                }
            }
        }
    }

    reflushAttrToUnit(unit: UnitModel, newAddAttrs: UnitAttribute, extraAttr: UnitAttribute, newestAttr: UnitAttribute) {
        const unitKeys = Object.keys(newAddAttrs);
        for (let i = 0; i < unitKeys.length; i ++) {
            const key = unitKeys[i];
            const oldValue = extraAttr[key];
            const value = newAddAttrs[key];
            /**
             * 该属性只用于属性计算
             */
            const newestValue = newestAttr[key];

            const numKey = MathUtil.toNumber(key);
            // 金币、木头、经验，实时性比较强，不能通过这个循环计时器计算
            switch (numKey) {
                case AttributeEnum.lifeRecovery:
                case AttributeEnum.magicRecovery:
                case AttributeEnum.coin:
                case AttributeEnum.wood:
                case AttributeEnum.magic:
                case AttributeEnum.life:
                case AttributeEnum.exp:
                case AttributeEnum.mainAttr: {
                    continue;
                }
                // 力量等属性通过额外值设置，需要更新额外值

                case AttributeEnum.agile:
                case AttributeEnum.strength:
                case AttributeEnum.intellect: {
                    if (oldValue !== value) {
                        let percentage
                        const oldMaxLife = Util.getTargetAttributeEnumByNotHero(unit, AttributeEnum.maxLife, true);
                        const oldMaxMagic = Util.getTargetAttributeEnumByNotHero(unit, AttributeEnum.maxMagic, true);
                        if (numKey === AttributeEnum.strength) {
                            percentage = unit.lifePercent
                        } else if (numKey === AttributeEnum.intellect) {
                            percentage = unit.manaPercent
                        }
                        const agile = newAddAttrs[AttributeEnum.agile];
                        const strength = newAddAttrs[AttributeEnum.strength];
                        const intellect = newAddAttrs[AttributeEnum.intellect];
                        Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.agile, agile, true);
                        Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.strength, strength, true);
                        Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.intellect, intellect, true);
                        if (numKey === AttributeEnum.strength) {
                            const maxLife = Util.getTargetAttributeEnumByNotHero(unit, AttributeEnum.maxLife, true);
                            const newLife = maxLife * percentage;
                            Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.life, newLife);
                            newestAttr[AttributeEnum.life] = newLife;
                            newestAttr[AttributeEnum.maxLife] = newestAttr[AttributeEnum.maxLife] + maxLife - oldMaxLife;
                        } else if (numKey === AttributeEnum.intellect) {
                            const maxMagic = Util.getTargetAttributeEnumByNotHero(unit, AttributeEnum.maxMagic, true);
                            const newMagic = maxMagic * percentage;
                            Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.magic, newMagic);
                            newestAttr[AttributeEnum.magic] = newMagic;
                            newestAttr[AttributeEnum.maxMagic] = newestAttr[AttributeEnum.maxMagic] + maxMagic - oldMaxMagic;
                        }
                    }
                    continue;
                }
                // case AttributeEnum.attack: {
                //     if (oldValue !== value) {
                //         Util.setTargetAttributeEnumToNotHero(unit, numKey, value, true);
                //     }
                //     continue;
                // }
                // 像生命值魔法值这种属性需要设置成更新之后的值
                case AttributeEnum.maxLife: {
                    if (oldValue !== value) {
                        const percentage = unit.lifePercent;
                        Util.setTargetAttributeEnumToNotHero(unit, numKey, newestValue);
                        const newLife = newestValue * percentage;
                        Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.life, newLife);
                        newestAttr[AttributeEnum.life] = newLife;
                    }
                    continue;
                }
                case AttributeEnum.maxMagic: {
                    if (oldValue !== value) {
                        const percentage = unit.manaPercent;
                        Util.setTargetAttributeEnumToNotHero(unit, numKey, newestValue);
                        const newMagic = newestValue * percentage;
                        Util.setTargetAttributeEnumToNotHero(unit, AttributeEnum.magic, newMagic);
                        newestAttr[AttributeEnum.magic] = newMagic;
                        // Logger.toastError(`魔法值变化了 ${unit.strId} ${unit.name} ${unit.mana} ${newMagic} ${percentage}`)
                    }
                    continue;
                }
                default: {
                    if (oldValue !== value) {
                        Util.setTargetAttributeEnumToNotHero(unit, numKey, newestValue, false);
                    }
                    continue;
                }
            }
        }
    }


    /**
     * 需要计算出百分比的值 加到 newAddAtts 中
     * @param unit
     * @param newAddAttrs
     * @param newestAttr
     */
    caluAttrP(unit: UnitModel, newestAttr: UnitAttribute, newAddAttrs: UnitAttribute) {
        const unitKeys = Object.keys(newAddAttrs);
        for (let i = 0; i < unitKeys.length; i ++) {
            const key = unitKeys[i];

            const normalKey = AttrPercentMap[AttributeEnum[key]];
            if (normalKey) {
            } else {
                continue;
            }

            const numKey = MathUtil.toNumber(key);
            const numNormalKey = MathUtil.toNumber(AttributeEnum[normalKey]);
            // 金币、木头、经验，实时性比较强，不能通过这个循环计时器计算
            switch (numKey) {
                // 主属性百分比在主属性计算时已经计算过了  这里不需要再次进行计算
                case AttributeEnum.mainAttrP: {
                    continue;
                }
                case AttributeEnum.strP:
                case AttributeEnum.agiP:
                case AttributeEnum.intP:
                case AttributeEnum.realHurtP:
                case AttributeEnum.attackP:
                case AttributeEnum.lawP:
                case AttributeEnum.maxLifeP:
                case AttributeEnum.maxMagicP:
                case AttributeEnum.defenseP:
                case AttributeEnum.magicResistanceP:
                case AttributeEnum.lifeRecoveryP:
                case AttributeEnum.magicRecoveryP: {
                    let pValue = 0;
                    if (newestAttr[key]) {
                        pValue = newestAttr[key];
                    }

                    const nValue = newestAttr[numNormalKey]
                    const percentValue = nValue * (pValue / 100);

                    newAddAttrs[numNormalKey] = newAddAttrs[numNormalKey] + percentValue;

                    continue;
                }
            }
        }
    }

    lifeAndMagicRecovery(unit: UnitModel) {
        if (unit?.isAliveInBattle()) {
        } else {
            return;
        }
        const ret = UnitUtil.getAttribute(unit)
        const newestUnitAttr = ret.newestAttribute;
        const subExtraAttr = ret.subExtraAttr;

        let bChange = false;

        const player = PlayerFactory.getInstance().getUnitOwner(unit);

        const lifeRecovery = newestUnitAttr[AttributeEnum.lifeRecovery];
        if (!CodeUtil.isNullOrZero(lifeRecovery)) {
            Util.caluAbilityAttr(unit, player, undefined, newestUnitAttr, undefined, AttributeEnum.life, undefined, lifeRecovery, 0, CaluType.add, false, true, 'lifeRecovery');
        }
        const magicRecovery = newestUnitAttr[AttributeEnum.magicRecovery];
        if (!CodeUtil.isNullOrZero(magicRecovery)) {
            Util.caluAbilityAttr(unit, player, undefined, newestUnitAttr, undefined, AttributeEnum.magic, undefined, magicRecovery, 0, CaluType.add, false, true, 'magicRecovery');
        }
        //
        const coinInterval = newestUnitAttr[AttributeEnum.coinInterval];
        if (!CodeUtil.isNullOrZero(coinInterval)) {
            Util.caluAbilityAttr(unit, player, undefined, newestUnitAttr, undefined, AttributeEnum.coin, undefined, coinInterval, 0, CaluType.add, false, true, 'coinInterval');
        }

        const woodInterval = newestUnitAttr[AttributeEnum.woodInterval];
        if (!CodeUtil.isNullOrZero(woodInterval)) {
            Util.caluAbilityAttr(unit, player, undefined, newestUnitAttr, undefined, AttributeEnum.wood, undefined, woodInterval, 0, CaluType.add, false, true, 'woodInterval');
        }

        const strInterval = newestUnitAttr[AttributeEnum.strInterval];
        if (!CodeUtil.isNullOrZero(strInterval)) {
            bChange = true;
            Util.caluAbilityAttr(unit, player, undefined, subExtraAttr, subExtraAttr, AttributeEnum.strength, undefined, strInterval, 0, CaluType.add, false, false, 'strInterval');
        }

        const agiInterval = newestUnitAttr[AttributeEnum.agiInterval];
        if (!CodeUtil.isNullOrZero(agiInterval)) {
            bChange = true;
            Util.caluAbilityAttr(unit, player, undefined, subExtraAttr, subExtraAttr, AttributeEnum.agile, undefined, agiInterval, 0, CaluType.add, false, false, 'agiInterval');
        }

        const intInterval = newestUnitAttr[AttributeEnum.intInterval];
        if (!CodeUtil.isNullOrZero(intInterval)) {
            bChange = true;
            Util.caluAbilityAttr(unit, player, undefined, subExtraAttr, subExtraAttr, AttributeEnum.intellect, undefined, intInterval, 0, CaluType.add, false, false, 'intInterval');
        }

        const attackInterval = newestUnitAttr[AttributeEnum.attackInterval];
        if (!CodeUtil.isNullOrZero(attackInterval)) {
            bChange = true;
            Util.caluAbilityAttr(unit, player, undefined, subExtraAttr, subExtraAttr, AttributeEnum.attack, undefined, attackInterval, 0, CaluType.add, false, false, 'attackInterval');
        }

        const maxLifeInterval = newestUnitAttr[AttributeEnum.maxLifeInterval];
        if (!CodeUtil.isNullOrZero(maxLifeInterval)) {
            bChange = true;
            Util.caluAbilityAttr(unit, player, undefined, subExtraAttr, subExtraAttr, AttributeEnum.maxLife, undefined, maxLifeInterval, 0, CaluType.add, false, false, 'maxLifeInterval');
        }

        const maxMagicInterval = newestUnitAttr[AttributeEnum.maxMagicInterval];
        if (!CodeUtil.isNullOrZero(maxMagicInterval)) {
            bChange = true;
            Util.caluAbilityAttr(unit, player, undefined, subExtraAttr, subExtraAttr, AttributeEnum.maxMagic, undefined, maxMagicInterval, 0, CaluType.add, false, false, 'maxMagicInterval');
        }

        if (bChange) {
            this.addCaluAttributeTaskByUnit(unit, false, '自动回复')
        }
    }

    addSummoneds(unit: UnitModel, activeUnit?: UnitModel, usePatro = true) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            player.data[PlayerDataKey.summoned].push(unit);
            if (activeUnit && usePatro) {
                this.addPatro(activeUnit, unit);
            }
        }
    }
    addPatro(master: UnitModel, unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            player.data[PlayerDataKey.patro].push(unit);

            unit.data[UnitDataKey.master] = master;
            const addPatroId = Configer.getFinal(FinalKey.addPatro)
            const cancelPatroId = Configer.getFinal(FinalKey.cancelPatro)
            unit.replaceAbility(addPatroId, cancelPatroId)
        }

    }
    inPatro(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const index = player.data[PlayerDataKey.patro].indexOf(unit);
            if (index >= 0) {
                return true
            } 
        }
        return false;
    }
    removePatro(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const index = player.data[PlayerDataKey.patro].indexOf(unit);
            if (index >= 0) {
                player.data[PlayerDataKey.patro].splice(index, 1);
                const addPatroId = Configer.getFinal(FinalKey.addPatro)
                const cancelPatroId = Configer.getFinal(FinalKey.cancelPatro)
                unit.replaceAbility(cancelPatroId, addPatroId)
            }  
        }
    }
    isInSummoneds(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const summoned = player.data[PlayerDataKey.summoned];
            if (summoned) {
            } else {
                return false;
            }
            for (let i = 0; i < summoned.length; i++) {
                const tempUnit = summoned[i];
                if (unit === tempUnit) {
                    return true
                }
            }
        }
        return false;
    }
    // 只是移除巡逻队，不操作原单位
    removeSummoneds(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const summoned = player.data[PlayerDataKey.summoned];
            if (summoned) {
                const index = summoned.indexOf(unit);
                if (index >= 0) {
                    player.data[PlayerDataKey.summoned].splice(index, 1);
                }
            }
        }
    }
    // 只是移除羁绊缓存，不操作原单位
    // removeUnitCombinations(unit: UnitModel) {
    //     const player = PlayerFactory.getInstance().getUnitOwner(unit);
    //     if (player?.isValid()) {
    //         const unitCombinations = player.data[PlayerDataKey.unitCombination];
    //         if (unitCombinations) {
    //             const strId = DataRouterUtil.getUnitStr(unit);
    //             if (strId && unitCombinations[strId]) {
    //                 unitCombinations[strId] = undefined
    //             }
    //         }
    //     }
    // }
    // 移除并杀死单位
    reflushSummonedByUnitId(player: PlayerModel, unitId: string, maxNum: number) {
        if (player?.isValid()) {
            const summoned = player.data[PlayerDataKey.summoned];
            if (summoned) {
                let currentNum = 0;
                for (let i = summoned.length - 1; i >= 0; i--) {
                    const unit = summoned[i];
                    // const strId = DataRouterUtil.getUnitStr(unit);
                    const strId = unit.strId;
                    if (strId === unitId) {
                        currentNum = currentNum + 1;
                        if (currentNum >= maxNum) {
                            summoned.splice(i, 1);
                            if (unit?.isValid()) {
                                this.unitDeadRemove(unit);
                            }
                        }
                    }
                }
            }
        }
    }
    clearSummoneByEffectId(player: PlayerModel, effectId: string) {
        const effectData = DataRouter.getEffectByEffectId(effectId);
        if (effectData) {
            const unitId = effectData.summon_id;
            this.reflushSummonedByUnitId(player, unitId, 0);
        } else {
            Logger.toastError(`不能通过不存在的召唤技能刷新召唤物 ${effectId}`)
        }

    }
    getNewAddAttribute (unit: UnitModel): UnitAttribute {
        const player = PlayerFactory.getInstance().getUnitOwner(unit)
        const newUnitAttr = new UnitAttribute();
        let newAttrs: Attrs =  { unitAttr: newUnitAttr, playerAttr: undefined }
        const enmuAttr = Configer.getConfig(ConfigKey.trackAttr) as string;

        const abilityRequest: BroadcastRequest = { broadcastType: BroadcastType.caluAbilityAttributes, controllerNo: ControllerNo.AbilityController, data: { unit } }
        const abilityRet = ControllerBroadcast.getInstance().broadcast(abilityRequest);
        if (abilityRet.status = BroadcastResponseStatus.success) {
            const attrs: Attrs = abilityRet.data.attribute;
            Logger.toastAttr(`从技能获取的属性:${AttributeEnum[enmuAttr]}: ${attrs.unitAttr[enmuAttr]}`);
            if (attrs.playerAttr) {
                Logger.toastAttr(`从技能获取的玩家属性:${AttributeEnum[enmuAttr]}: ${attrs.playerAttr[enmuAttr]}`);
            }

            newAttrs = Util.sumAttributes(newAttrs, attrs, CaluType.add);
        }

        const goodRequest: BroadcastRequest = { broadcastType: BroadcastType.caluGoodAttributes, controllerNo: ControllerNo.GoodController, data: { unit } }
        const goodRet = ControllerBroadcast.getInstance().broadcast(goodRequest);
        if (goodRet.status = BroadcastResponseStatus.success) {
            const attrs: Attrs = goodRet.data.attribute;
            Logger.toastAttr(`从物品获取的单位属性:${AttributeEnum[enmuAttr]}: ${attrs.unitAttr[enmuAttr]}`);
            if (attrs.playerAttr) {
                Logger.toastAttr(`从物品获取的玩家属性:${AttributeEnum[enmuAttr]}: ${attrs.playerAttr[enmuAttr]}`);
            }
            newAttrs = Util.sumAttributes(newAttrs, attrs, CaluType.add);
        }

        const achievementRequest: BroadcastRequest = { broadcastType: BroadcastType.caluAchievementAttributes, controllerNo: ControllerNo.AchievementController, data: { unit } }
        const achievementRet = ControllerBroadcast.getInstance().broadcast(achievementRequest);
        if (achievementRet.status = BroadcastResponseStatus.success) {
            const attrs = achievementRet.data.attribute;
            Logger.toastAttr(`从成就获取的单位属性:${AttributeEnum[enmuAttr]}: ${attrs.unitAttr[enmuAttr]}`);
            if (attrs.playerAttr) {
                Logger.toastAttr(`从成就获取的玩家属性:${AttributeEnum[enmuAttr]}: ${attrs.playerAttr[enmuAttr]}`);
            }
            newAttrs = Util.sumAttributes(newAttrs, attrs, CaluType.add);
        }

        const combinationAttr = this.caluUnitCombination(unit);
        Logger.toastAttr(`从羁绊获取的单位属性:${AttributeEnum[enmuAttr]}: ${combinationAttr.unitAttr[enmuAttr]}`, player);
        if (combinationAttr.playerAttr) {
            Logger.toastAttr(`从羁绊获取的玩家属性:${AttributeEnum[enmuAttr]}: ${combinationAttr.playerAttr[enmuAttr]}`, player);
        }

        newAttrs = Util.sumAttributes(newAttrs, combinationAttr, CaluType.add);
        const initAttr = this.getInitAttributes(unit);
        if (initAttr) {
            Logger.toastAttr(`从单位获取的单位属性:${AttributeEnum[enmuAttr]}: ${initAttr.unitAttr[enmuAttr]}`, player);
            if (initAttr.playerAttr) {
                Logger.toastAttr(`从单位获取的玩家属性:${AttributeEnum[enmuAttr]}: ${initAttr.playerAttr[enmuAttr]}`, player);
            }
            newAttrs = Util.sumAttributes(newAttrs, initAttr, CaluType.add);
        }


        let playerAttrs = newAttrs.playerAttr;
        if (playerAttrs) {
            const handleId = unit.getHandleId();
            player.data[PlayerDataKey.attributes][handleId] = playerAttrs
        }

        // this.changePlayerAttr(player);
        const newAttr = this.updateUnitAttrByPlayerAttr(newAttrs.unitAttr, player)

        // Logger.toastAttr(`新增加的单位属性:${AttributeEnum[enmuAttr]}: ${newAttr[enmuAttr]}`, player);
        return  newAttr;
    }
    updateUnitAttrByPlayerAttr(unitAttr: UnitAttribute, player: PlayerModel): PlayerAttribute {
        const playerAttrs = player.data[PlayerDataKey.attributes];
        let newAttr = new PlayerAttribute();
        if (playerAttrs) {
            CodeUtil.mapObj(playerAttrs, (handleId, attr) => {
                newAttr = Util.sumPlayerAttribute(attr, newAttr, CaluType.add);
            })
        }
        newAttr = Util.sumPlayerAttribute(unitAttr, newAttr, CaluType.add);
        return newAttr;
    }
    // changePlayerAttr(player: PlayerModel) {
    //     let bChange = false;
    //
    //     const oldAttr = this.getPlayerAttr(player);
    //     const sumAttr = this.getSumPlayerAttribute(player);
    //
    //     const keys  = Object.keys(sumAttr);
    //     for (let i = 0; i < keys.length; i ++) {
    //         const key = keys[i];
    //         const oldValue = oldAttr ? oldAttr[key] : 0;
    //         const newValue = sumAttr ? sumAttr[key] : 0;
    //         if (oldValue === newValue) {
    //         } else {
    //             bChange = true;
    //         }
    //     }
    //     if (bChange) {
    //         this.addCaluAttributeTaskByPlayer(player, AchievementBeneficiaryType.hero)
    //         this.addCaluAttributeTaskByPlayer(player, AchievementBeneficiaryType.pet)
    //         this.addCaluAttributeTaskByPlayer(player, AchievementBeneficiaryType.summon)
    //     }
    // }
    addAttributeUnit (unit: UnitModel, useAffix: boolean) {
        // 初始化单位的装备属性增益系统
        let change = this.initAttribute(unit, useAffix);

        const initAttr = this.getInitAttributes(unit);
        unit.data[UnitDataKey.newestAttribute] = Util.cloneAttr(initAttr.unitAttr);
        unit.data[UnitDataKey.extraAttribute] = Util.cloneAttr(initAttr.unitAttr);
        unit.manaPercent = 1;
        unit.lifePercent = 1;
        this.reflushAttrToUnit(unit, unit.data[UnitDataKey.extraAttribute], new UnitAttribute(), unit.data[UnitDataKey.newestAttribute]);
        this.initGrowAttr(unit);

        return change
    }

    initGrowAttr(unit: UnitModel) {
        const unitData = unit?.template;
        const lv = unit?.level;
        if (unitData) {
            const growthAttrId = unitData.growthAttrId;
            const attr = DataRouterUtil.getAttrByAttrInterface(growthAttrId, lv - 1);
            unit.data[UnitDataKey.subAttribute] = attr ? attr.unitAttr : new UnitAttribute();
        }
    }

    getAttrByAffixData(affixData: AffixData) {
        return  DataRouterUtil.getAttrByAttrInterface(affixData?.attrId, 1);
    }
    getAffixAttr(unit: UnitModel) {
        let unitAttr = new UnitAttribute();
        let attrs: Attrs = { unitAttr, playerAttr: undefined };
        const affixs = unit.data[UnitDataKey.unitAffix];
        for (let i = 0; i < affixs.length; i++) {
            const affixId = affixs[i];
            const affixData = DataRouter.getAffixDatasByAffixID(affixId);
            const tempAttr = this.getAttrByAffixData(affixData);
            attrs = Util.sumAttributes(attrs, tempAttr, CaluType.add);
        }
        return attrs;
    }
    initAffixByDiff() {
        const difficult = GlobalEnv.difficult;
        const difficultDatas = DataRouter.getDifficultDatas();
        const difficultData = difficultDatas[difficult];
        let retAffixs: string[] = [];
        const affixGroupStr = difficultData.affix_group;
        const affixNumStr = difficultData.affix_num;
        const affixGroupArr = CodeUtil.getArrBySplit(affixGroupStr);
        const affixNumArr = CodeUtil.getArrBySplit(affixNumStr);
        for (let i = 0; i < affixGroupArr.length; i++) {
            const affixGroup = affixGroupArr[i];
            const affixNum = DataRouterUtil.getNumByArr(affixNumArr, i);
            const tempArr = DataRouterUtil.getAffixByGroupId(affixGroup, affixNum);
            retAffixs.push(...tempArr);
        }
        GlobalEnv.affixs = retAffixs;
        // GlobalEnv.affixs = [ 'af10' ];
    }
    addAffixByDiff(unit: UnitModel) {
        let change = false;
        if (unit) {
            const retAffixs = GlobalEnv.affixs;
            unit.data[UnitDataKey.unitAffix] = retAffixs;
            for (let i = 0; i < retAffixs.length; i ++) {
                change = true;
                const affixId = retAffixs[i];
                const affixData = DataRouter.getAffixDatasByAffixID(affixId);
                const skills = affixData?.skill;
                const skillArr = CodeUtil.getArrBySplit(skills);
                for (let skillIndex = 0; skillIndex < skillArr.length; skillIndex++) {
                    const skillId = skillArr[skillIndex];
                    unit.addAbility(skillId)
                }
            }
        }
        return change;
    }
    getInitAttributes (unit: UnitModel) {
        if (unit) {
            const initAttrs = unit.data[UnitDataKey.initAttribute];
            return initAttrs; 
        }
    }
    setInitAttributes (unit: UnitModel, attr: Attrs) {
        if (unit) {
            unit.data[UnitDataKey.initAttribute] = attr;
        }
    }
    deleteAttributeUnit (unit: UnitModel) {
        // 初始化单位的装备属性增益系统
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const handleId = unit.getHandleId();
            player.data[PlayerDataKey.attributes][handleId] = undefined;
            player.data[PlayerDataKey.catchCompensate][handleId] = undefined;
            UnitController.getInstance().removeFightTime(unit);
            const request: BroadcastRequest = { broadcastType: BroadcastType.deleteAbilityAttr, controllerNo: ControllerNo.AbilityController, data: { unit: unit } }
            ControllerBroadcast.getInstance().broadcast(request);
        }
    }
    clearUnitByPlayer (player: PlayerModel) {
        const hero = GlobalEnv.getHero(player);
        if (hero?.isValid()) {
            this.clearUnitByUnit(hero, true);
            GlobalEnv.addHero(player, undefined);
        }
        player.data[PlayerDataKey.petInfo] = []
        const pets = player.data[PlayerDataKey.pet];
        const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
        for (let i = 0; i < maxPetNum; i ++) {
            const pet = pets[i];
            if (pet?.isValid()) {
                this.clearUnitByUnit(pet, true);
            }
        }
        pets.length = 0;
        const box = GlobalEnv.getBox(player);
        if (box?.isValid()) {
            GlobalEnv.setBox(player, undefined);
            this.clearUnitByUnit(box, true);
        }

        const map = player.data[PlayerDataKey.generateUnitMap]
        CodeUtil.mapObj(map, (key, value) => {
            const unit = ModelFactory.getInstance().getModel(key, ModelType.unit) as UnitModel;
            if (unit?.isValid()) {
                this.clearUnitByUnit(unit, true);
            }
        })
        GlobalEnv.reflushMaxAttackNum();
    }
    removeHatredNum(unit: UnitModel) {
        const unitId = unit.getHandleId();
        const attackerMap = unit.data[UnitDataKey.hatredAttackerMap];
        CodeUtil.mapObj(attackerMap, (attackedId: string, bool: boolean) => {
            const attacked = ModelFactory.getInstance().getModel(attackedId, ModelType.unit) as UnitModel;
            if (attacked?.isValid()) {
                attacked.data[UnitDataKey.hatredAttackedMap][unitId] = undefined;
            }
        })
        const attackedMap = unit.data[UnitDataKey.hatredAttackedMap];
        CodeUtil.mapObj(attackedMap, (attackerId: string, hatredNum: number) => {
            const attacker = ModelFactory.getInstance().getModel(attackerId, ModelType.unit) as UnitModel;
            if (attacker?.isValid()) {
                attacker.data[UnitDataKey.hatredAttackerMap][unitId] = undefined;
            }
        });
        UnitController.hatredRegist[unitId] = undefined;
    }
    removeLastHurtUnit(unit: UnitModel) {
        PlayerFactory.getInstance().mapAllNotEndPlayingPlayer((player: PlayerModel) => {
            if (player.data[PlayerDataKey.lastHurtBoss]) {
                if (unit === player.data[PlayerDataKey.lastHurtBoss]) {
                    player.data[PlayerDataKey.lastHurtBoss] = undefined;
                }
            }
        })
    }
    clearUnitByUnit (unit: UnitModel, bExit: boolean = false) {
        this.removeFightTime(unit)
        this.cancelOneClickCall(unit);
        this.removeShareBoss(unit, bExit);
        this.removeEnemyUnit(unit);
        this.removeHatredNum(unit);
        this.removeLastHurtUnit(unit);
        GlobalEnv.removeAllAttackMonster(unit);
        this.clearGenerateData(unit);
        this.deleteAttributeUnit(unit);
        this.removeSummoneds(unit);
        this.removePatro(unit);
        this.removeUnitCombinationByUnit(unit);
        // this.removeUnitCombinations(unit);
        this.clearUnitPath(unit);

        TriggerFactory.getInstance().destoryUnit(unit);
    }
    addHero(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            GlobalEnv.addHero(player, unit);
        }
    }
    bindVestUser(vest: UnitModel, json: VestJson) {
        vest.data[UnitDataKey.vestJson] = json;
    }

    copyUnitState(unit: UnitModel, newUnit: UnitModel) {
        newUnit.data[UnitDataKey.unitAffix] = unit.data[UnitDataKey.unitAffix];
        newUnit.data[UnitDataKey.forbiddenCache] = unit.data[UnitDataKey.forbiddenCache];
        newUnit.data[UnitDataKey.hurtNum] = unit.data[UnitDataKey.hurtNum];
        newUnit.data[UnitDataKey.resumeNum] = unit.data[UnitDataKey.resumeNum];
        newUnit.data[UnitDataKey.resumeTime] = unit.data[UnitDataKey.resumeTime];
        newUnit.data[UnitDataKey.bBattle] = unit.data[UnitDataKey.bBattle];
        newUnit.data[UnitDataKey.master] = unit.data[UnitDataKey.master];
        newUnit.data[UnitDataKey.maxLvCache] = unit.data[UnitDataKey.maxLvCache];
    }

    changeUnit(unit:UnitModel, unitId: string, changeUnitType: ChangeUnitType, copyAbility: boolean = true) {
        // const attack = Util.getTargetAttributeValueByNotHero(unit, AttributeEnum.attack, false);
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const loc = unit.loc;
            let newUnit: UnitModel;
            let group;
            unit.show = false;
            const lifePercent = unit.lifePercent;
            const magicPercent = unit.manaPercent;
            const json: UnitJson = { id: unitId, e: unit.experience };

            const oldHero = GlobalEnv.getHero(player);
            const bHero = (oldHero === unit)

            // const oldBox = GlobalEnv.getBox(player);
            // const bBox = (oldBox === unit)

            if (changeUnitType === ChangeUnitType.hero) {
                group = this.createUnit(1, json, player, loc, 0, true);
            } else if (changeUnitType === ChangeUnitType.box) {
                group = this.createBox(unitId, player, loc, loc);
            } else {
                group = this.createUnit(1, json, player, loc, 0);
            }

            const isSummon = this.isInSummoneds(unit);
            const petIndex = this.getPetIndex(unit, player);

            Util.mapUnitArr(group, (unit) => {
                newUnit = unit;
            });

            this.copyUnitState(unit, newUnit);

            if (copyAbility) {
                const abilityRequest: BroadcastRequest = { broadcastType: BroadcastType.copyAbilitys, controllerNo: ControllerNo.AbilityController, data: { unit, newUnit } }
                ControllerBroadcast.getInstance().broadcast(abilityRequest);
            }
            const oldLevel = unit.level;

            const goodsRequest: BroadcastRequest = { broadcastType: BroadcastType.copyGoods, controllerNo: ControllerNo.GoodController, data: { unit, newUnit } }
            ControllerBroadcast.getInstance().broadcast(goodsRequest);

            newUnit.lifePercent = lifePercent;
            newUnit.manaPercent = magicPercent;

            const hero = GlobalEnv.getHero(player);

            if (isSummon) {
                this.addSummoneds(newUnit, hero);
            }
            if (petIndex >= 0) {
                const newPetInfo = newUnit.getJson();
                player.data[PlayerDataKey.petInfo][petIndex] = newPetInfo;
                player.data[PlayerDataKey.pet][petIndex] = newUnit;
                this.replacePet(hero, unit, newUnit)
            }
            if (bHero) {

                const request: BroadcastRequest = { broadcastType: BroadcastType.getAchHistory, controllerNo: ControllerNo.StoreController, data: { player: player } }
                const res = ControllerBroadcast.getInstance().broadcast(request);
                const achHistory: AchievementSerializeBody = res.data.achHistory;

                if (achHistory) {
                    const request: BroadcastRequest = { broadcastType: BroadcastType.loadApp, controllerNo: ControllerNo.AchievementController, data: { player: player, achHistory: achHistory } }
                    ControllerBroadcast.getInstance().broadcast(request);
                }
            }

            this.addCaluAttributeTaskByUnit(newUnit, false, '复制')

            this.unitDeadRemove(unit);

            if (isSummon || petIndex >= 0 || bHero) {
                this.addUnitCombinationByUnit(newUnit);
            }


            return newUnit;
            // const retQ = DataRouterUtil.getUnitAbilityIdByHotKey(unit, ConfigConstant.HotKeyQ);
            // const retW = DataRouterUtil.getUnitAbilityIdByHotKey(unit, ConfigConstant.HotKeyW);
            // const retE = DataRouterUtil.getUnitAbilityIdByHotKey(unit, ConfigConstant.HotKeyE);
            // const retR = DataRouterUtil.getUnitAbilityIdByHotKey(unit, ConfigConstant.HotKeyR);
            // if (retQ.abilityId) {
            //     abilityId = retQ.abilityId;
            //     level = retQ.level;
            //     UnitAddAbility(newUnit, FourCC(abilityId));
            //     SetUnitAbilityLevel(newUnit, abilityId, level)
            // }
            // if (retW.abilityId) {
            //     abilityId = retW.abilityId;
            //     level = retW.level;
            //     UnitAddAbility(newUnit, FourCC(abilityId));
            //     SetUnitAbilityLevel(newUnit, abilityId, level)
            // }
            // if (retE.abilityId) {
            //     abilityId = retE.abilityId;
            //     level = retE.level;
            //     UnitAddAbility(newUnit, FourCC(abilityId));
            //     SetUnitAbilityLevel(newUnit, abilityId, level)
            // }
            // if (retR.abilityId) {
            //     abilityId = retR.abilityId;
            //     level = retR.level;
            //     UnitAddAbility(newUnit, FourCC(abilityId));
            //     SetUnitAbilityLevel(newUnit, abilityId, level)
            // }
        }
    }
    replacePet(activeUnit: UnitModel, oldUnit: UnitModel, newUnit: UnitModel) {
        if (this.inOneClickCallPet(oldUnit)) {
            this.cancelOneClickCall(oldUnit);
            this.addToOneClickCall(newUnit);
        }
        if (!CodeUtil.isNullOrZero(oldUnit.data[UnitDataKey.bBattle])) {
            newUnit.data[UnitDataKey.bBattle] = 1;
        }
        if (this.inPatro(oldUnit)) {
            this.removePatro(oldUnit);
            this.addPatro(activeUnit, newUnit)
        }
    }
    resetUnit (unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        Logger.toastProduct(`英雄重生了！`, player);
        unit.level = 0;
    }
    addCaluAttributeTaskByUnit(unit: UnitModel, bForce: boolean, str: string) {
        if (unit?.isValid()) {
        } else {
            return;
        }
        if(GlobalEnv.isInBoxGroup(unit)) {
            return;
        }
        if (bForce) {
        } else {
            if (!CodeUtil.isNullOrZero(unit.data[UnitDataKey.bBattle])) {
            } else {
                return;
            }
        }

        if (DataRouterUtil.checkUnitUseAttr(unit)) {
            const id = unit.getHandleId();
            if (id) {
                UnitController.caluTaskMap.set(id, str);
                return;
            }
        }
    }
    clearCaluTask() {
        UnitController.caluTaskMap.forEach(
            (value, handleId, map) => {
                // const typeId = GetUnitTypeId(unit);
                // const strId = Util.toShortStrId(typeId);
                // const unitData = DataRouter.getUnitDataByUnitId(strId);
                const unitModel = ModelFactory.getInstance().getModel(handleId, ModelType.unit) as UnitModel;

                if (DataRouterUtil.checkUnitUseAttr(unitModel)) {
                    // const name = unitData.Name;
                    // Logger.toastError(`${unitModel.name} 计算属性，起因：${value}`);
                    this.caluAttribute(unitModel);
                }
            }
        )
        UnitController.caluTaskMap.clear();
    }
    addCaluAttributeTaskByPlayer(player: PlayerModel, unitType: AchievementBeneficiaryType) {
        switch (unitType) {
            case AchievementBeneficiaryType.hero:{
                const hero = GlobalEnv.getHero(player);
                this.addCaluAttributeTaskByUnit(hero, false, CaluAttrStr.ach);
                break;
            }
            case AchievementBeneficiaryType.pet:{
                const pets = player.data[PlayerDataKey.pet];
                const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
                for (let i = 0; i < maxPetNum; i++) {
                    const pet = pets[i];
                    this.addCaluAttributeTaskByUnit(pet, false, CaluAttrStr.ach);
                }
                break;
            }
            case AchievementBeneficiaryType.summon:{
                const summoneds = player.data[PlayerDataKey.summoned];
                const len = summoneds.length;
                for (let i = 0; i < len; i++) {
                    const summoned = summoneds[i];
                    this.addCaluAttributeTaskByUnit(summoned, false, CaluAttrStr.ach);
                }
                break;
            }
        }
    }
    getHandBookUnit(player: PlayerModel) {
        const petArr = this.getPetInfo(player);
        const unitInfos: UnitJson[] = [];
        for (let i = 0; i < petArr.length; i++) {
            const unitInfo = petArr[i];
            const id = unitInfo && unitInfo.id;
            if (id) {
                const str = DataRouterUtil.getUnitStrByUnitId(id);
                let bHas = false;
                for (let j = 0; j < unitInfos.length; j++) {
                    const handBookInfo = unitInfos[j];
                    const handBookId = handBookInfo.id;
                    if (handBookId === str) {
                        const handBookExp = handBookInfo.e;
                        const currentExp = unitInfo.e;
                        if (currentExp > handBookExp) {
                            handBookInfo.e = currentExp;
                        }
                        bHas = true;
                    }
                }
                if (bHas) {
                } else {
                    const handBookData = DataRouter.getHandBookDataByUnitId(str);
                    if (handBookData) {
                        const handBookUnitInfo: UnitJson = { id: handBookData.id, e: unitInfo.e }
                        unitInfos.push(handBookUnitInfo)
                    }
                }
            }
        }
        return unitInfos;
    }
    notify(request: BroadcastRequest): BroadcastResponse {
        const ret: BroadcastResponse = { controllerNo: this.getControllerNo(), data: {}, status: BroadcastResponseStatus.success };
        const type = request.broadcastType;
        const data = request.data;
        switch (type) {
            case BroadcastType.serialize: {
                const player: PlayerModel = data.player;
                const serializeBody: SerializeBody = this.serialize(player);
                ret.data = { serializeBody };
                break;
            }
            case BroadcastType.changeUnit: {
                const unitId: string = data.unitId;
                const unit: UnitModel = data.unit;
                const changeUnitType: ChangeUnitType = data.changeUnitType;
                const newUnit = this.changeUnit(unit, unitId, changeUnitType);
                ret.data = { unit: newUnit };
                break;
            }
            case BroadcastType.resetUnit: {
                const unit: UnitModel = data.unit;
                this.resetUnit(unit);
                break;
            }
            case BroadcastType.checkToInitMonster: {
                const monsterData: GenerateData  = data.monsterData;
                const num: number = monsterData.num;
                this.checkToInitMonster(monsterData, num);
                break;
            }
            case BroadcastType.getAttribute: {
                const unit: UnitModel = data.unit;
                const attr = UnitUtil.getAttribute(unit);
                ret.data = { attr };
                break;
            }
            case BroadcastType.isInSummon: {
                const unit: UnitModel = data.unit;
                const isInSummoneds = this.isInSummoneds(unit);
                ret.data = { isInSummoneds };
                break;
            }
            case BroadcastType.isInPets: {
                const unit: UnitModel = data.unit;
                const isInPets = this.isInPets(unit);
                ret.data = { isInPets };
                break;
            }
            case BroadcastType.addCaluTaskByUnit: {
                const unit: UnitModel = data.unit;
                const type: CaluAttrStr = data.caluAttrStr;
                this.addCaluAttributeTaskByUnit(unit, false, type);
                break;
            }
            case BroadcastType.addCaluTaskByPlayer: {
                const player: PlayerModel = data.player;
                const type: AchievementBeneficiaryType = data.type;
                this.addCaluAttributeTaskByPlayer(player, type);
                break;
            }
            case BroadcastType.clearSummoneByEffectId: {
                const player: PlayerModel = data.player;
                const effectId: string = data.effectId;
                this.clearSummoneByEffectId(player, effectId);
                break;
            }
            case BroadcastType.addPet: {
                const unitId = data.unitId;
                const player: PlayerModel = data.player;
                const success = this.addPet(player, { id: unitId, e: 0 });
                ret.data = { success };
                break;
            }
            case BroadcastType.resumePet: {
                const player: PlayerModel = data.player;
                this.resumePets(player);
                break;
            }
            case BroadcastType.resumeHero: {
                const player: PlayerModel = data.player;
                this.resumeHero(player);
                break;
            }
            case BroadcastType.checkHasPet: {
                const player: PlayerModel = data.player;
                const unitId = data.unitId;
                const bHas = this.checkHasPet(player, unitId);
                ret.data = { bHas };
                break;
            }
            case BroadcastType.getHandBookUnit: {
                const player: PlayerModel = data.player;
                const unitInfos = this.getHandBookUnit(player);
                ret.data = { unitInfos };
                break;
            }
            case BroadcastType.createUnit: {
                const player: PlayerModel = data.player;
                const id: string = data.unitId;
                const unitNum: number = data.unitNum;
                const unitJson: UnitJson = { id: id, e: 0 }
                const loc: LocModel = data.loc;
                this.createUnit(unitNum, unitJson, player, loc, 0);
                break;
            }
            case BroadcastType.clearUnitByPlayer: {
                const player: PlayerModel = data.player;
                this.clearUnitByPlayer(player);
                break;
            }
            case BroadcastType.useTransData: {
                const units: UnitModel[] = data.units;
                const transData: TransData = data.transData;
                const bRandom: boolean = data.bRandom;
                this.useTransData(units, transData, bRandom);
                break;
            }
            default:
                ret.status = BroadcastResponseStatus.failed;
                break;
        }
        return ret;
    }
    resumePets(player: PlayerModel) {
        const pets: UnitModel[] = this.getPets(player)
        const maxPetNum: number = Configer.getConfig(ConfigKey.maxPetNum);
        for (let i = 0; i < maxPetNum; i++) {
            const pet = pets[i];
            if (pet.isValid()) {
                this.resumePet(player, pet)
            }
        }
    }
    resumePet(player: PlayerModel, pet: UnitModel) {
        const resumeType = Configer.getConfig(ConfigKey.petResumeType);
        if (resumeType === PetResumeType.null) {
            return;
        }
        if (pet) {
            if (pet.isAlive()) {
            } else {
                pet.data[UnitDataKey.resumeTime] = 0;
                const hero = GlobalEnv.getHero(player);
                let loc: LocModel;
                switch (resumeType) {
                    case PetResumeType.default: {
                        loc = hero.loc;
                        break;
                    }
                    case PetResumeType.home: {
                        loc = player.startLocation;
                        break;
                    }
                }
                if (loc) {
                    const unitId  = pet.strId;
                    const usedFood = player.usedFood;
                    const allFood = player.allFood;

                    const success = pet.revive(loc, true);
                    if (success) {
                        if (pet?.name) {
                            Logger.toastProduct(`宠物 ${pet.name} 已复活`, player);
                        } else {
                            Logger.toastError(`宠物 ${pet.strId}  未定义名称`, player);
                        }
                    } else {
                        Logger.toastProduct('宠物因未知原因删除导致复活失败！不过没关系，贴心的作者为您重新生成了一个！您可以加入玩家群，将录像发送给作者来排查原因！', player)
                    }

                    const bBattle = pet.data[UnitDataKey.bBattle];
                    if (!CodeUtil.isNullOrZero(bBattle)) {
                        const ret = this.addUnitCheckFood(unitId, allFood, usedFood);
                        if (ret) {
                            this.addUnitCombinationByUnit(pet);
                            return;
                        }
                    }

                    this.callBack(pet, player);
                }
            }
        }
    }
    addPetInfo(player: PlayerModel, index: number, unitInfo: UnitJson) {
        if (player?.isValid()) {
            player.data[PlayerDataKey.petInfo][index] = unitInfo;

            const str = DataRouterUtil.getUnitStrByUnitId(unitInfo.id);
            const handBookData = DataRouter.getHandBookDataByUnitId(str);
            if (handBookData) {
                const handBookId = handBookData.id;
                const handBookUnitInfo: UnitJson = { id: handBookId, e: 1 }
                const request: BroadcastRequest = { broadcastType: BroadcastType.initHandBook, controllerNo: ControllerNo.AchievementController, data: { handBookUnitInfo: handBookUnitInfo, player: player } }
                ControllerBroadcast.getInstance().broadcast(request);
            }
        }
    }
    addPet(player: PlayerModel, unitJson: UnitJson, autoCallBack: boolean = false) {
        const hero = GlobalEnv.getHero(player);
        if (hero) {
            const firstNullIndex = this.getFirstPetNullIndex(player);
            if (firstNullIndex === -1) {
                Logger.toastProduct('您的兽栏已满', player)
                return false;
            }

            const petInfo: UnitJson = unitJson

            if (autoCallBack) {
            } else {
                const unitData = DataRouter.getUnitDataByUnitId(unitJson?.id);
                if (unitData) {
                    const name = unitData.Name;
                    if (name) {
                        Logger.toastProduct(`获得了 ${name}`, player)
                    }
                }
            }

            this.addPetInfo(player, firstNullIndex, petInfo);
            this.callPet(hero, firstNullIndex, autoCallBack);
            return true;
        }
    }

    serialize(player: PlayerModel): UnitSerializeBody {
        const unitSerializeBody: UnitSerializeBody = player.data[PlayerDataKey.storeCurrent][ControllerNo.UnitController];
        const archiveIndex = player.data[PlayerDataKey.archiveIndex];

        this.mapUnitSerialize(archiveIndex, SerializeLoadType.current_global, (key, index, type) => {
            let unit: UnitModel;

            switch (type) {
                case UnitLoadType.hi:
                case UnitLoadType.bi:{
                    if (type === UnitLoadType.bi) {
                        unit = GlobalEnv.getBox(player);
                    } else if (type === UnitLoadType.hi) {
                        unit = GlobalEnv.getHero(player);
                    }

                    if (unit?.isValid()) {
                        unitSerializeBody[key] = unit.getJson();
                    }
                    break;
                }
                case UnitLoadType.pi: {
                    unitSerializeBody[key] = this.getPetInfoByIndex(player, index);
                    break;
                }
            }
        })

        return unitSerializeBody
    }
    getPetInfo = (player: PlayerModel) => {
        if (player?.isValid()) {
            const petInfos = player.data[PlayerDataKey.petInfo];
            return petInfos;
        }
        return [];
    }
    getPets = (player: PlayerModel) => {
        if (player?.isValid()) {
            const pets = player.data[PlayerDataKey.pet];
            return pets;
        }
        return [];
    }
    getPetByIndex = (player: PlayerModel, index: number): UnitModel => {
        if (player?.isValid()) {
            const pet = player.data[PlayerDataKey.pet][index];
            return pet  
        }
    }
    getPetInfoByIndex = (player: PlayerModel, index: number) => {
        if (player?.isValid()) {
            const petInfo = player.data[PlayerDataKey.petInfo][index];
            if (petInfo && petInfo.id) {
                return petInfo;
            }
        }
        return {};
    }

    // setPetInfoByIndex = (playerId: number, index: number, unitInfo: UnitInfo) => {
    //     UnitController.petInfoGroups[playerId][index] = unitInfo;
    //     const unitId = unitInfo.id
    //     const unitData = DataRouter.getUnitDataByUnitId(unitId);
    //     if (unitData) {
    //         const name = unitData.Name;
    //         if (name) {
    //             Util.loggerInDev(`获得了 ${name}`, LogType.product, ToastType.text, player);
    //         }
    //     }
    // }

    // 修改单位绿字的接口
    getExtraAttr(unit: UnitModel) {
        if (unit) {
            const extraAttr = unit.data[UnitDataKey.extraAttribute];
            if (extraAttr) {
                return extraAttr;
            }
        }
        return new UnitAttribute();
    }
    releasePet(player: PlayerModel, index: number) {
        const hero = GlobalEnv.getHero(player);

        const isVaild = hero.isValid();

        if (isVaild && player?.isValid()) {
            const petInfo = this.getPetInfoByIndex(player, index)
            if (petInfo && petInfo.id) {
            } else {
                Logger.toastProduct('当前栏位没有宠物！', player)
                return false;
            }
            const pet = this.getPetByIndex(player, index)
            const isShow = pet?.show;
            player.data[PlayerDataKey.pet][index] = undefined;
            player.data[PlayerDataKey.petInfo][index] = {} as UnitJson;
            if (pet) {
                const isVaild = pet?.isAliveInBattle();
                let loc
                if (isVaild && isShow) {
                    loc = pet.loc;
                } else {
                    loc = hero.loc;
                }
                pet.mapItems(
                    (item) => {
                        pet.dropItem(item, loc);
                        return false;
                    }
                )
                // const owner = PlayerFactory.getInstance().getUnitOwner(pet);
                // if (owner.isNotEndPlaying()) {
                //     this.removeUnitCombinationByUnit(pet);
                // }
                this.unitDeadRemove(pet);
            }
            return true;
        } else {
            Logger.toastProduct('请选择英雄后再操作宠物！', player)
            return false;
        }
    }
    // -1 为没找到
    getPetIndex(unit: UnitModel, player: PlayerModel): number {
        if (player?.isValid()) {
            const petArr = player.data[PlayerDataKey.pet];
            let num = -1
            const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
            for (let i = 0; i < maxPetNum; i ++) {
                const pet = petArr[i];
                if (unit && unit === pet) {
                    num = i;
                    break;
                }
            }
            return num;
        }
        return 0;
    }
    getFirstPetNullIndex(player: PlayerModel): number {
        if (player?.isValid()) {
            const petInfoArr = player.data[PlayerDataKey.petInfo];
            let num = -1
            const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
            for (let i = 0; i < maxPetNum; i ++) {
                const petInfo = petInfoArr[i];
                if (petInfo && petInfo.id) {
                } else {
                    num = i;
                    break;
                }
            }
            return num;
        }
        return 0;
    }
    // 返回false代表没有剩余人口
    addUnitCheckFood(unitId: string, allFood: number, usedFood: number) {
        let unitData = DataRouter.getUnitDataByUnitId(unitId);
        let useFood = 0;
        if (unitData && unitData.fused) {
            useFood = unitData.fused;
        }
        const otherNum = allFood - usedFood;
        const retFood = otherNum - useFood;
        if (retFood < 0) {
            return false;
        }
        return true;
    }
    callPet(activeUnit: UnitModel, index: number, autoBack: boolean = false) {
        const isVaild = activeUnit;
        const player = PlayerFactory.getInstance().getUnitOwner(activeUnit);
        if (player?.isValid()) {
            const allFood = player.allFood;
            const usedFood = player.usedFood;
            if (isVaild) {
                const petInfo = this.getPetInfoByIndex(player, index) as UnitJson;

                if (petInfo && petInfo.id) {
                    let loc = activeUnit.loc;
                    const petCallType: number = Configer.getConfig(ConfigKey.petCallType);
                    switch (petCallType) {
                        case PetCallType.home: {
                            loc = player.startLocation;
                            break;
                        }
                    }
                    let pet = this.getPetByIndex(player, index);
                    const petValid = pet?.isAlive();
                    let bCall = false;
                    if (petValid) {
                        if (pet.show) {
                        } else {
                            player.moveUnit(pet, loc, false);
                            // player.toBeUnitOwner(pet, false)
                            pet.setUseFood(true);
                            pet.show = true;
                            bCall = true;
                        }
                    } else {
                        const petDead: number = Configer.getConfig(ConfigKey.petDead);
                        if (pet) {
                            if (!CodeUtil.isNullOrZero(petDead)) {
                                Logger.toastProduct('宠物已经死亡，请等待复活', player)
                            }
                        } else {
                            const group = UnitController.getInstance().createUnit(1, petInfo, player, loc, 0);
                            Util.mapUnitArr(group, (unit) => {
                                const enumUnit = unit;
                                player.data[PlayerDataKey.pet][index] = enumUnit;
                                // this.addPatro(enumUnit);
                                pet = enumUnit;
                                this.addCaluAttributeTaskByUnit(pet, true, '初始化宠物')

                                const autoPetPatro: number = Configer.getConfig(ConfigKey.autoPetPatro);
                                if (pet && !CodeUtil.isNullOrZero(autoPetPatro)) {
                                    this.addPatro(activeUnit, pet)
                                }
                            })
                            bCall = true;
                        }
                    }

                    if (bCall) {
                        let unitId = '';
                        if (petInfo && petInfo.id) {
                            unitId = petInfo.id;
                        }
                        let needCallBack = false;

                        const callPetLvLimit: number = Configer.getConfig(ConfigKey.callPetLvLimit);
                        if (pet.level > (activeUnit.level + callPetLvLimit)) {
                            needCallBack = true;
                            Logger.toastProduct(`宠物等级不能超过英雄${callPetLvLimit}级 ！`, player)
                        } else if (autoBack) {
                            needCallBack = true;
                        } else {
                            const ret = this.addUnitCheckFood(unitId, allFood, usedFood);
                            if (ret) {
                                pet.data[UnitDataKey.bBattle] = 1;
                                this.addUnitCombinationByUnit(pet);
                            } else {
                                Logger.toastProduct('召唤数量已经达到最大！', player)
                                needCallBack = true;
                            }
                        }

                        if (needCallBack) {
                            bCall = false
                            this.callBack(pet, player)
                        }
                    }

                    return bCall
                } else {
                    Logger.toastProduct('当前位置没有宠物，请前往捕捉!', player)
                }
            } else {
                Logger.toastProduct('请选择英雄后再操作宠物!', player)
            }
        }
        return false
    }
    callBack(unit: UnitModel, player: PlayerModel) {
        const index = this.getPetIndex(unit, player);
        if (index >= 0) {
            unit.show = false;
            this.removeUnitCombinationByUnit(unit);
            unit.setUseFood(false);
            unit.data[UnitDataKey.bBattle] = 0;
            // PlayerFactory.getInstance().getPlayer(neutralHostilityId).toBeUnitOwner(unit, false)
            return true;
        }
        return false;
    }
    setPlayerUnitCatchCompensate(player: PlayerModel, unit: UnitModel, num: number | undefined) {
        const handleId = unit?.getHandleId();
        if (handleId) {
            const catchCompensates = player.data[PlayerDataKey.catchCompensate];
            catchCompensates[handleId] = num;
        }
    }
    getPlayerUnitCatchCompensate(player: PlayerModel, unit: UnitModel) {
        const handleId = unit?.getHandleId();
        if (handleId) {
            const catchCompensates = player.data[PlayerDataKey.catchCompensate];
            const num = catchCompensates[handleId];
            if (num) {
                return  num;
            }
        }
        return 0;
    }
    catchPet(activeUnit: UnitModel, unit: UnitModel, player: PlayerModel, index: number, exp: number = 0) {
        const unitId = unit?.strId;
        const petData = DataRouterUtil.getPetDataByCatchId(unitId);
        
        if (petData) {
            const petIds = petData.get_pet;
            const petIdArr = CodeUtil.getArrBySplit(petIds);
            const petWeights = petData.get_pet_weight;
            const petWeightArr = CodeUtil.getArrBySplit(petWeights);

            const datas: WeightIDData[] = [];

            for (let i = 0; i < petIdArr.length; i++) {
                const data: WeightIDData = { id: petIdArr[i], weight: DataRouterUtil.getNumByArr(petWeightArr, i), probability: 0 };
                datas.push(data);
            }

            const ret = DataRouterUtil.getDataModelByWeight(player, datas, 1);
            let getId = ret && ret[0] && ret[0].id;

            if (getId) {
            } else {
                getId = unitId;
            }

            const petInfo: UnitJson = { id: getId, e: exp };
            // petInfo.t = GlobalEnv.difficult;

            this.addPetInfo(player, index, petInfo);
            // const oldPet = UnitController.pets[playerId][index];
            // this.addAttributeUnit(unit);
            // if (oldPet) {
            //     this.petDead(unit);
            // }
            unit.show = false;
            unit.kill();
            this.callPet(activeUnit, index);
            const pet = this.getPetByIndex(player, index);
            // this.addToOneClickCall(pet);
            const autoPetCall: number = Configer.getConfig(ConfigKey.autoPetCall);
            if (pet && !CodeUtil.isNullOrZero(autoPetCall)) {
            } else {
                this.callBack(pet, player)
            }
        }
        // UnitController.pets[playerId][index] = unit;
    }

    inOneClickCallPet(pet: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(pet);
        if (player?.isValid()) {
            const oneClicks: UnitModel[] = player.data[PlayerDataKey.oneClickCalls];

            const index = oneClicks.indexOf(pet);
            if (index >= 0) {
                return true
            }
        }
        return false;
    }

    oneClickCallPet(player: PlayerModel) {
        if (player?.isValid()) {
            const oneClicks: UnitModel[] = player.data[PlayerDataKey.oneClickCalls];
            if (oneClicks.length > 0) {
            } else {
                Logger.toastProduct('还没有设置一键召唤，请在宠物技能栏中设置！');
                return;
            }
            const hero = GlobalEnv.getHero(player)
            for (let i = 0; i < oneClicks.length; i++) {
                const unit = oneClicks[i];
                const index = this.getPetIndex(unit, player);
                this.callPet(hero, index)
            }
        }
    }
    oneClickCallBack(player: PlayerModel) {
        if (player?.isValid()) {
            const pets: UnitModel[] = player.data[PlayerDataKey.pet];
            const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum)
            for (let i = 0; i < maxPetNum; i++) {
                const pet = pets[i];
                this.callBack(pet, player)
            }
        }
    }
    addToOneClickCall(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const oneClicks: UnitModel[] = player.data[PlayerDataKey.oneClickCalls];

            let usedFood = 0;
            for (let i = 0; i < oneClicks.length; i++) {
                const tempUnit = oneClicks[i];
                const unitData = DataRouterUtil.getUnitDataByUnit(tempUnit);
                const useFood = unitData.fused;
                if (!CodeUtil.isNullOrZero(usedFood)) {
                    usedFood = usedFood + useFood;
                }
            }

            const unitData = DataRouterUtil.getUnitDataByUnit(unit);
            const currentUseFood = unitData.fused;

            const allNum = player.allFood;
            const otherNum = allNum - usedFood;
            const retFood = otherNum - currentUseFood;
            if (retFood < 0) {
                return false;
            }

            oneClicks.push(unit);
            const addToOneClickCallId = Configer.getFinal(FinalKey.addToOneClickCall)
            const cancelToOneClickCall = Configer.getFinal(FinalKey.cancelToOneClickCall)
            unit.replaceAbility(addToOneClickCallId, cancelToOneClickCall)
            return true
        }
        return false;
    }
    cancelOneClickCall(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const oneClicks: UnitModel[] = player.data[PlayerDataKey.oneClickCalls];

            const index = oneClicks.indexOf(unit)
            if (index >= 0) {
                oneClicks.splice(index, 1)
                const addToOneClickCallId = Configer.getFinal(FinalKey.addToOneClickCall)
                const cancelToOneClickCall = Configer.getFinal(FinalKey.cancelToOneClickCall)
                unit.replaceAbility(cancelToOneClickCall, addToOneClickCallId)
                return true
            }
        }
        return false;
    }

    addEnemyUnit(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const handleId = unit.getHandleId();
            player.data[PlayerDataKey.enemyUnits][handleId] = unit.strId;
        }
    }
    removeEnemyUnit(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isValid()) {
            const handleId = unit.getHandleId();
            const enemyUnits = player.data[PlayerDataKey.enemyUnits];
            const name = enemyUnits[handleId]
            if (name) {
                enemyUnits[handleId] = undefined;
            }
        }
    }

    backHome(player: PlayerModel) {
        const startLoc = player.startLocation;
        const hero = GlobalEnv.getHero(player);
        player.moveUnit(hero, startLoc, true)
    }


    initGenerate = (generateData: GenerateData, delayTime: number, num: number) => {
        const difficult = generateData.difficult;
        const currentDifficult = GlobalEnv.difficult;
        const difficultData = DataRouterUtil.getDifficultData(currentDifficult);
        if (!CodeUtil.isNullOrZero(difficult)) {
            if (difficult === currentDifficult) {
            } else {
                return;
            }
        }

        if (!CodeUtil.isNullOrZero(generateData.mode)) {
            if (generateData.mode === difficultData.mode) {
            } else {
                return;
            }
        }

        const playNum = generateData.playerNum;
        const currentPlayNum = PlayerFactory.getInstance().getPlayingPlayerNum();
        if (!CodeUtil.isNullOrZero(playNum)) {
            if (playNum === currentPlayNum) {
            } else {
                return;
            }
        }

        const callBack = function () {
            if (GlobalEnv.getAllBeEnd()) {
                return;
            }
            const area = generateData.area;
            const areaArr = CodeUtil.getArrBySplit(area);
            const path = generateData.path;
            const pathArr = CodeUtil.getArrBySplit(path);
            const checkPlayer = generateData.checkPlayer;
            const monsterIds = generateData.monster_id;
            const monsterIdArr = CodeUtil.getArrBySplit(monsterIds);
            const existence_time = generateData.existence_time;
            const type  = generateData.type;
            for (let i = 0; i < areaArr.length; i++) {
                // tempPlayer是玩家约束
                let tempPlayer: PlayerModel;
                if (!CodeUtil.isNullOrZero(checkPlayer)) {
                    tempPlayer = PlayerFactory.getInstance().getPlayer(i);
                    const isPlaying = tempPlayer?.isNotEndPlaying();
                    if (isPlaying) {
                    } else {
                        continue;
                    }
                }
                const monsterId = monsterIdArr[i];
                const rectName = areaArr[i];
                const pathId = pathArr[i];
                const rect = ModelFactory.getInstance().getRectModel(rectName);
                const locCenter = rect.getCenterLocModel();
                const textSize = Configer.getConfig(ConfigKey.baseTextSize) as number;
                let players: PlayerModel[] = [];
                let bEnemy = false;
                switch (type) {
                    case GenerateDataType.scan:
                    case GenerateDataType.item:
                    case GenerateDataType.enemy:
                    case GenerateDataType.teamEnemy:
                    case GenerateDataType.toast:
                    case GenerateDataType.timer:
                    case GenerateDataType.text:
                    case GenerateDataType.ui:{
                        if (type === GenerateDataType.teamEnemy) {
                            const enemyTeam = Configer.getConfig(ConfigKey.enemyTeam) as number;
                            const enemyPlayer = GlobalEnv.getTeamFreePlayer(enemyTeam);
                            bEnemy = true;
                            if (enemyPlayer?.isValid()) {
                                players.push(enemyPlayer);
                            }
                        } else if (generateData.param) {
                            const playerIdArr = CodeUtil.getArrBySplit(generateData.param);
                            for (let i = 0; i < playerIdArr.length; i++) {
                                const playerId = playerIdArr[i];
                                const numPlayerId = MathUtil.toNumber(playerId);
                                const neutralHostilityId = Configer.getConfig(ConfigKey.neutralHostilityId)
                                const neutralInvincibility = Configer.getConfig(ConfigKey.neutralInvincibility)
                                let tempId = neutralHostilityId;
                                if (numPlayerId === neutralHostilityId) {
                                    tempId = neutralHostilityId;
                                } else if (numPlayerId === neutralInvincibility) {
                                    tempId = neutralInvincibility
                                } else if (numPlayerId >= 0) {
                                    tempId = numPlayerId;
                                }
                                const tempPlayer = PlayerFactory.getInstance().getPlayer(tempId);
                                players.push(tempPlayer);
                            }
                        }

                        let item;
                        if (monsterId) {
                            let face_angle = generateData.face_angle;


                            const unitArr: UnitModel[] = [];
                            const itemArr: ItemModel[] = [];
                            for (let numIndex = 0; numIndex < num; numIndex++) {

                                let locType = generateData.locType;
                                let loc = rect.getRandomLocModel();
                                if (locType === LocType.center) {
                                    loc = rect.getCenterLocModel();
                                }

                                if (type === GenerateDataType.enemy || type === GenerateDataType.teamEnemy) {
                                    players.forEach((unitPlayer, index, array) => {
                                        // unitPlayer 是生成哪个玩家的单位
                                        const unitJson: UnitJson = { id: monsterId, e: 0 };
                                        let realFace = face_angle;
                                        if (realFace) {
                                        } else {
                                            realFace = MathUtil.random(0, 360);
                                        }

                                        const arr = UnitController.getInstance().createUnit(1, unitJson, unitPlayer, loc, realFace, false, tempPlayer);
                                        const pathData = DataRouter.getPathDataByPathId(pathId);
                                        for (let i = 0; i < arr.length; i++) {
                                            const unit = arr[i];
                                            const newGenerateData: GenerateData = JSON.parse(JSON.stringify(generateData));
                                            newGenerateData.realLoc = loc;
                                            UnitController.unitItem_generateData_Map.set(unit, newGenerateData);
                                            if (bEnemy) {
                                                UnitController.getInstance().addEnemyUnit(unit);
                                            }

                                            unitArr.push(unit);
                                            unit.removeGuardPosition();
                                            UnitController.getInstance().unitUsePath(rectName, pathData, unit);

                                            const unitData = DataRouterUtil.getUnitDataByUnit(unit);
                                            if (unitData.unit_type === UnitType.shareBoss) {
                                                UnitController.getInstance().addShareBoss(unit);
                                            }
                                        }
                                    })
                                } else if (type === GenerateDataType.item) {
                                    const itemJson: ItemJson = { id: monsterId, n: 0, af: '', cd: 0, lv: 0 }
                                    item = ModelFactory.getInstance().createItemInPoint(itemJson, loc);
                                    const newGenerateData: GenerateData = JSON.parse(JSON.stringify(generateData));
                                    newGenerateData.realLoc = loc;
                                    UnitController.unitItem_generateData_Map.set(item, newGenerateData);
                                    itemArr.push(item);
                                }

                                const bTipRange = generateData.tip_display;
                                if (!CodeUtil.isNullOrZero(bTipRange)) {
                                    PlayerFactory.getInstance().getLocalPlayer().signal(loc);
                                }
                            }
                            if (!CodeUtil.isNullOrZero(existence_time)) {
                                const endCallBack = () => {
                                    const disappearPath = Configer.getResource(ResourceKey.disappearPath);
                                    const disappearTime = Configer.getFinal(FinalKey.disappearTime);
                                    for (let i = 0; i < unitArr.length; i++) {
                                        const unit = unitArr[i];
                                        if (unit?.isAlive()) {
                                            TimeController.addSpecialEffectFun(unit, disappearPath, disappearTime, unit.loc, '', SpecialEffectType.point, 0)
                                            UnitController.getInstance().unitDeadRemove(unit);
                                        }
                                    }
                                    for (let i = 0; i < itemArr.length; i++) {
                                        const item = itemArr[i];
                                        UnitController.getInstance().itemDeadRemove(item);
                                    }
                                }
                                const existenceOpt = new TimeControllerOpt(undefined, endCallBack, 1);
                                TimeController.getInstance().startTimer({time: existence_time, opt: existenceOpt});
                            }
                            let tipStr = generateData.tip;
                            if (tipStr) {
                                if (monsterId && !CodeUtil.isNullOrZero(generateData.tipShowName)) {
                                    const unitData = DataRouter.getUnitDataByUnitId(monsterId);
                                    if (unitData) {
                                        tipStr = `${tipStr}<${unitData.Name}>`
                                    }
                                }
                                const localPlayer = PlayerFactory.getInstance().getLocalPlayer();
                                Logger.toastProduct(tipStr, localPlayer)
                            }
                        }
                        if (type === GenerateDataType.scan) {
                            players.forEach((player, index, array) => {
                                const fogmodifier = player.removeFog(rect);
                                if (!CodeUtil.isNullOrZero(existence_time)) {
                                    const endCallBack = () => {
                                        player.addFog(fogmodifier);
                                    }
                                    const existenceOpt = new TimeControllerOpt(undefined, endCallBack, 1);
                                    TimeController.getInstance().startTimer({time: existence_time, opt: existenceOpt});
                                }
                            })
                        }

                        const text = generateData.text;
                        let textTag: TexttagModel;
                        if (text) {
                            textTag = ModelFactory.getInstance().createTexttag(text, textSize, locCenter);
                            if (text && !CodeUtil.isNullOrZero(existence_time)) {
                                const endCallBack = () => {
                                    ModelFactory.getInstance().destroy(textTag, ModelType.text);
                                }
                                const existenceOpt = new TimeControllerOpt(undefined, endCallBack, 1);
                                TimeController.getInstance().startTimer({time: existence_time, opt: existenceOpt});
                            }
                        }
                        break;
                    }
                }
            }

            const uiPath = generateData.UI_path;
            if (uiPath) {
                const size = { w: generateData.UI_w, h: generateData.UI_h }
                const pos = { x: generateData.UI_x, y: generateData.UI_y }
                const request: BroadcastRequest = { broadcastType: BroadcastType.showTextUi, controllerNo: ControllerNo.GuiController, data: { uiName: ResourceKey.generate, path: uiPath, size: size, pos: pos, autoClose: false } }
                ControllerBroadcast.getInstance().broadcast(request);
            }
            const timerText = generateData.timerText;
            if (timerText && !CodeUtil.isNullOrZero(existence_time)) {
                const endCallBack = () => {
                }
                const existenceOpt = new TimeControllerOpt(timerText, endCallBack, 1);
                TimeController.getInstance().startTimer({time: existence_time, opt: existenceOpt});
            }

        }
        const timerOpt = new TimeControllerOpt(undefined, callBack, 1);
        TimeController.getInstance().startTimer({time: delayTime, opt: timerOpt});
    }
    getGenerateDataCurrentNum(monsterData: GenerateData) {
        const currentId = monsterData.id;
        let sum = 0;
        UnitController.unitItem_generateData_Map.forEach((value, key, map) => {
            const tempId = value.id;
            if (tempId === currentId) {
                sum = sum + 1;
            }
        })
        return sum;
    }

    checkToInitMonster(monsterData: GenerateData, num: number, bResume: boolean = false) {
        const allNum = monsterData.num;
        const currentNum = UnitController.getInstance().getGenerateDataCurrentNum(monsterData);
        const autoResume = monsterData.auto_resume;
        if (bResume) {
            if (!CodeUtil.isNullOrZero(autoResume)) {
            } else {
                return;
            }
        }
        if (allNum > currentNum) {
            let tempNum = 1;
            if (!CodeUtil.isNullOrZero(num)) {
                tempNum = num;
            }
            let delayTime = 0;
            if (monsterData.interval_time) {
                delayTime = monsterData.interval_time;
            }
            UnitController.getInstance().initGenerate(monsterData, delayTime, tempNum);
        }
    }
    reflushFightTime = (player: PlayerModel) => {
        const fightTimeMap = UnitUtil.getClearFightTimesByPlayer(player);
        CodeUtil.mapObj(fightTimeMap, (handleId, time) => {
            const unit = ModelFactory.getInstance().getModel(handleId, ModelType.unit) as UnitModel;
            if (unit?.isValid()) {
                if (time && time >= 0) {
                    const newTime = time - 1;
                    fightTimeMap[handleId] = newTime;
                    if (newTime < 0) {
                        const size = this.removeFightTime(unit);
                        // 倒计时到期则移除附加属性
                        this.endFightCallBack(player, unit, size);
                    }
                }
            }
        })
    }
    removeFightTime = (unit: UnitModel) => {
        const player = PlayerFactory.getInstance().getUnitOwner(unit)
        const clearFightTimes = UnitUtil.getClearFightTimesByPlayer(player);
        const handleId = unit?.getHandleId();
        if (handleId) {
            clearFightTimes[handleId] = undefined
        }
        const size = CodeUtil.getObjSize(clearFightTimes);
        return size;
    }
    caluUnitCombination(unit: UnitModel): Attrs {
        let unitAttr = new UnitAttribute();
        let playerAttr = new PlayerAttribute();
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        const playerCheck = player?.isNotEndPlaying();
        let attrs = { unitAttr, playerAttr };
        if (playerCheck) {
        } else {
            return attrs;
        }
        // const strId = DataRouterUtil.getUnitStr(unit);
        const unitCombinations = this.getUnitCombinationsByUnit(unit);

        for (let i = 0; i < unitCombinations.length; i++) {
            const combination = unitCombinations[i];
            CodeUtil.mapObj(combination, (key, ret: CombitionRet) => {
                const combitionAttrs = ret.attrs;
                if (combitionAttrs) {
                    attrs = Util.sumAttributes(attrs, combitionAttrs, CaluType.add);
                }
                return false;
            })
        }

        return attrs;
    }
    getUnitCombinationsByUnit(unit: UnitModel): CombinationInfo[] {
        const arr: CombinationInfo[] = [];
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        const playerCheck = player?.isNotEndPlaying();
        if (playerCheck) {
        } else {
            return arr;
        }
        const strId = DataRouterUtil.getUnitStr(unit);
        const unitCombination = player.data[PlayerDataKey.unitCombination];
        const combination = unitCombination[strId];
        arr.push(combination);
        const bAll = BAllStr;
        const bAllCombination = unitCombination[bAll];
        arr.push(bAllCombination);
        return arr;
    }
    // 只用来做ui显示的model
    // 羁绊列表需要从短到长 把羁绊id翻到外面  单位id翻到里面
    getAllUnitCombinationBodyByPlayer(player: PlayerModel): CombinationUIInfo {
        const playerCheck = player?.isNotEndPlaying();
        if (playerCheck) {
        } else {
            return;
        }
        const unitCombination = player.data[PlayerDataKey.unitCombination];

        const combinationIds: { [key: string]: CombitionRet } = {};
        const unitIds: { [key: string]: number } = {};
        const unitArr: UnitModel[] = this.getCombinationUnitArr(player);

        // 遍历所有单位判断羁绊
        for (let unitIndex = 0; unitIndex < unitArr.length; unitIndex++) {
            const unit = unitArr[unitIndex];
            const unitId = DataRouterUtil.getUnitStr(unit);

            // 如果单位重复只取一个
            const num = unitIds[unitId];

            if (num && num >= 0) {
                unitIds[unitId] = num + 1
                continue;
            } else {
                unitIds[unitId] = 1;
            }
            const combinationDatas = DataRouter.getCombinationDatasByItemId(unitId);
            if (combinationDatas) {
            } else {
                continue;
            }
            for (let combinationIndex = 0; combinationIndex < combinationDatas.length; combinationIndex++) {
                const combinationData = combinationDatas[combinationIndex];
                if (combinationData) {
                    // const currentName = combinationData.name;
                    const combinationId = combinationData.id;
                    const rets = combinationIds[combinationId];
                    if (rets) {
                        continue;
                    } else {
                        if (unitCombination[unitId] && unitCombination[unitId][combinationId]) {
                            combinationIds[combinationId] = unitCombination[unitId][combinationId]
                        }
                    }
                }
            }
        }
        return { combinationIds: combinationIds, ids: unitIds };
    }

    checkHasPet(player: PlayerModel, unitId: string) {
        const petInfos = this.getPetInfo(player);
        const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
        const unitUseFamily = Configer.getConfig(ConfigKey.unitUseFamily);
        for (let i = 0; i < maxPetNum; i ++) {
            const petInfo = petInfos[i];
            if (petInfo && petInfo.id) {
                if (!CodeUtil.isNullOrZero(unitUseFamily)) {
                    const unitData = DataRouter.getUnitDataByUnitId(petInfo.id);
                    const familyId = unitData?.family_tpye;
                    if (familyId === unitId) {
                        return true
                    }
                } else {
                    if (petInfo.id === unitId) {
                        return true;
                    }
                }
            }
        }
        return false;
    }


    getCombinationUnitArr(player: PlayerModel) {
        const ret: UnitModel[] = [];
        const pet = Util.getAllBattlePetAndSummoned(player);
        const hero = GlobalEnv.getHero(player);
        ret.push(hero);
        ret.push(...pet);
        return ret
    }

    reflushUnitCombination(player: PlayerModel, calu: boolean = true) {
        const request: BroadcastRequest = { broadcastType: BroadcastType.updateGui, controllerNo: ControllerNo.GuiController, data: { guiId: GuiId.combinationBar, player: player } }
        ControllerBroadcast.getInstance().broadcast(request);

        const unitArr = this.getCombinationUnitArr(player);
        for (let i = 0; i < unitArr.length; i++) {
            const unit = unitArr[i];
            this.reflushUnitCombinationSkill(player, unit);
            if (!calu) {
                continue;
            }
            this.addCaluAttributeTaskByUnit(unit, false, '刷新羁绊');
        }
    }
    reflushUnitCombinationSkill(player: PlayerModel, unit: UnitModel) {
        if (player?.isValid()) {
            const strId = DataRouterUtil.getUnitStr(unit);
            const unitCombinations = player.data[PlayerDataKey.unitCombination];
            const unitCombinationDatas = DataRouter.getCombinationDatasByItemId(strId);
            if (unitCombinationDatas) {
            } else {
                return;
            }
            for (let i = 0; i < unitCombinationDatas.length; i++) {
                const unitCombinationData = unitCombinationDatas[i];
                const combinationId = unitCombinationData.id;
                const addSkills = unitCombinationData.add_skill;
                const delSkills = unitCombinationData.del_skill;
                const unitCombination = unitCombinations[strId];
                const hasRet = unitCombination && unitCombination[combinationId];
                if (hasRet) {
                    const addSkillArr = CodeUtil.getArrBySplit(addSkills);
                    const delSkillArr = CodeUtil.getArrBySplit(delSkills);
                    for (let skillIndex = 0; skillIndex < addSkillArr.length; skillIndex++) {
                        const addSkillId = addSkillArr[skillIndex];
                        const removeSkillId = delSkillArr[skillIndex];
                        unit.replaceAbility(removeSkillId, addSkillId)
                    }
                } else {
                    const addSkillArr = CodeUtil.getArrBySplit(addSkills);
                    const delSkillArr = CodeUtil.getArrBySplit(delSkills);
                    for (let skillIndex = 0; skillIndex < addSkillArr.length; skillIndex++) {
                        const addSkillId = addSkillArr[skillIndex];
                        const removeSkillId = delSkillArr[skillIndex];
                        unit.replaceAbility(addSkillId, removeSkillId)
                    }
                }
            }
        }
    }

    addUnitCombinationByUnit(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isNotEndPlaying()) {
        } else {
            return false;
        }
        const strId = DataRouterUtil.getUnitStr(unit)
        const unitCombinationDatas = DataRouter.getCombinationDatasByItemId(strId);
        if (unitCombinationDatas) {
        } else {
            return;
        }
        const unitCombinations = player.data[PlayerDataKey.unitCombination]

        const combinationInfos: MaterialInfo[] = [];


        const unitArr = this.getCombinationUnitArr(player);
        for (let i = 0; i < unitArr.length; i++) {
            const unit = unitArr[i];
            const unitId = DataRouterUtil.getUnitStr(unit);
            if (unitId) {
                const combinationInfo: MaterialInfo = {
                    id: unitId,
                    num: 1
                }
                combinationInfos.push(combinationInfo);
            }
        }
        const unitUseLadder = Configer.getConfig(ConfigKey.unitUseLadder);
        const checkRets = DataRouterUtil.checkToCombitionByMaterialInfos(combinationInfos, unitCombinationDatas, unitUseLadder);

        // 将符合条件的所有单位的Id都添加羁绊Id
        const bChange = this.reflushUnitCombitionByCombitionRet(unitCombinations, checkRets);

        this.reflushUnitCombination(player, bChange)
    }
    removeUnitCombinationByUnit(unit: UnitModel) {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (player?.isNotEndPlaying()) {
        } else {
            return false;
        }

        const strId = DataRouterUtil.getUnitStr(unit)
        const unitCombinationDatas = DataRouter.getCombinationDatasByItemId(strId);
        const bAllCombination = player.data[PlayerDataKey.unitCombination][BAllStr];
        if (unitCombinationDatas || bAllCombination) {
        } else {
            return false;
        }

        const combination = player.data[PlayerDataKey.unitCombination][strId];

        if (combination) {
        } else {
            return false;
        }

        let change = true;

        const unitArr = this.getCombinationUnitArr(player);
        const combinationInfos: MaterialInfo[] = [];
        for (let i = 0; i < unitArr.length; i++) {
            const unit = unitArr[i];
            const unitId = DataRouterUtil.getUnitStr(unit);
            if (unitId) {
                if (unitId === strId) {
                    change = false;
                    break;
                }
                const combinationInfo: MaterialInfo = {
                    id: unitId,
                    num: 1
                }
                combinationInfos.push(combinationInfo);
            }
        }

        if (change) {
        } else {
            return false;
        }
        const unitUseLadder = Configer.getConfig(ConfigKey.unitUseLadder);
        const checkRets = DataRouterUtil.checkToCombitionByMaterialInfos(combinationInfos, unitCombinationDatas, unitUseLadder);

        change = false;

        CodeUtil.mapObj(bAllCombination, (combitionId, ret: CombitionRet) => {
            const tempRet = checkRets[combitionId];
            if (tempRet) {
                let needChange = false;
                if (ret) {
                    if (tempRet.successNum !== ret.successNum) {
                        needChange = true;
                    }
                }
                if (needChange) {
                    change = true;
                    bAllCombination[combitionId] = tempRet;
                }
            } else {
                let bHas = false;
                const combitionData = DataRouter.getCombinationDataByCombinationId(combitionId);
                const unitIds = combitionData.material_id;
                const unitIdArr = CodeUtil.getArrBySplit(unitIds);
                for (let i = 0; i < unitIdArr.length; i++) {
                    const unitId = unitIdArr[i];
                    if (unitId === strId) {
                        bHas = true;
                    }
                }
                if (bHas) {
                    change = true;
                    bAllCombination[combitionId] = undefined;
                }
            }
        })

        CodeUtil.mapObj(combination, (combitionId, ret: CombitionRet) => {
            const tempRet = checkRets[combitionId];
            if (tempRet) {
                let needChange = false;
                if (ret) {
                    if (tempRet.successNum !== ret.successNum) {
                        needChange = true;
                    }
                }
                if (needChange) {
                    change = true;
                    combination[combitionId] = tempRet;
                }
            } else {
                let bHas = false;
                const combitionData = DataRouter.getCombinationDataByCombinationId(combitionId);
                const unitIds = combitionData.material_id;
                const unitIdArr = CodeUtil.getArrBySplit(unitIds);
                for (let i = 0; i < unitIdArr.length; i++) {
                    const unitId = unitIdArr[i];
                    if (unitId === strId) {
                        bHas = true;
                    }
                }
                if (bHas) {
                    change = true;
                    combination[combitionId] = undefined;
                }
            }
        })

        // if (combination) {
        //     const keys = Object.keys(combination);
        //     if (keys && keys.length && keys.length > 0) {
        //     } else {
        //         unitCombination[strId] = undefined;
        //     }
        // }

        // player.data[PlayerDataKey.unitCombination] = {};
        // const unitCombinations = player.data[PlayerDataKey.unitCombination];
        // const bRemove = this.reflushUnitCombitionByCombitionRet(unitCombinations, checkRets);

        this.reflushUnitCombination(player, change);
        return change
    }

    reflushUnitCombitionByCombitionRet(unitCombinations: { [key: string]: CombinationInfo }, checkRets: { [mixId: string]: CombitionRet }) {
        let bChange = false;
        // 将符合条件的所有单位的Id都添加羁绊Id
        CodeUtil.mapObj(checkRets, (key, value) => {
            const combination = DataRouter.getCombinationDataByCombinationId(key);
            if (combination) {
                const bAll = combination.bAll;
                const addCombinationFun = (str: string) => {
                    let unitCombination: CombinationInfo;
                    if (unitCombinations[str]) {
                    } else {
                        bChange = true;
                        unitCombinations[str] = {};
                    }
                    unitCombination = unitCombinations[str]
                    unitCombination[key] = value;
                }
                if (!CodeUtil.isNullOrZero(bAll)) {
                    const bAllStr = BAllStr
                    addCombinationFun(bAllStr);
                } else {
                    const unitIdStrs = combination.material_id;
                    const unitIdArr = CodeUtil.getArrBySplit(unitIdStrs);
                    for (let i = 0; i < unitIdArr.length; i++) {
                        const unitId = unitIdArr[i];
                        addCombinationFun(unitId);
                    }
                }
            }
            return false;
        })
        return bChange;
    }
    clearUnitPath(unit: UnitModel) {
        UnitController.pathRegist.forEach(
            (value, key, map) => {
                const hasUnit = value.has(unit);
                if (hasUnit) {
                    value.delete(unit)
                }
            }
        )
    }
    registUnitPath = (rangeName: string, unit: UnitModel, end: string, order: OrderStr) => {
        if (rangeName) {
            let map = UnitController.pathRegist.get(rangeName);
            if (map) {
            } else {
                map = new Map<UnitModel, PathCache>();
                UnitController.pathRegist.set(rangeName, map);
                const pathCallBack = function (this: void, rect: RectModel, activeUnit: UnitModel, player: PlayerModel) {
                    activeUnit.data[UnitDataKey.pathCache] = rect.nikeName;
                    UnitController.getInstance().exeUnitPath(activeUnit);
                }
                TriggerFactory.getInstance().registerUnitEnterRect(rangeName, EnterType.path, pathCallBack)
            }
            const newCache = { end: end, order: order }
            map.set(unit, newCache);
        }
    }
    exeUnitPath(unit: UnitModel) {
        const rangeName = unit?.data[UnitDataKey.pathCache]
        const map = UnitController.pathRegist.get(rangeName);
        if (map) {
            const cache = map.get(unit);
            if (cache) {
                const end = cache.end;
                const orderStr = cache.order;
                const endRect = ModelFactory.getInstance().getRectModel(end);
                if (endRect) {
                    const locModel = endRect.getCenterLocModel();
                    unit.issueOrderAt(orderStr, locModel)
                }
            }
        }
    }
    addPlayerUnitMap(player: PlayerModel, unit: UnitModel) {
        const handleId = unit?.getHandleId();
        if (handleId) {
            player.data[PlayerDataKey.generateUnitMap][handleId] = true;
        }
    }

    registUnitForbidden (rangeName: string, unitId: string, text: string, callBack: (text: string, unit: UnitModel, player: PlayerModel) => void) {
        let map = UnitController.forbiddenRegist.get(rangeName);
        if (map) {
        } else {
            map = new Map<string, any>();
            UnitController.forbiddenRegist.set(rangeName, map);
            const pathCallBack = function (this: void, rect: RectModel, activeUnit: UnitModel, player: PlayerModel) {
                activeUnit.data[UnitDataKey.forbiddenCache] = rect.nikeName;
                UnitController.getInstance().exeUnitForbidden(activeUnit, player);
            }
            TriggerFactory.getInstance().registerUnitEnterRect(rangeName, EnterType.forbidden, pathCallBack)
        }
        map.set(unitId, { text: text, fun: callBack } );
    }
    exeUnitForbidden(unit: UnitModel, player: PlayerModel) {
        const rangeName = unit?.data[UnitDataKey.forbiddenCache]
        const map = UnitController.forbiddenRegist.get(rangeName);
        if (map) {
            const unitId = unit.strId;
            if (unitId) {
                const cache = map.get(unitId);
                if (cache) {
                    const text = cache.text;
                    const fun = cache.fun;
                    fun(text, unit, player);
                }
            }
        }
    }
    unitUsePath(startRange: string, pathData: PathData, unit: UnitModel) {
        if (pathData) {
            const delay = pathData.delay;
            const delayFun = () => {
                const pathStrs = pathData.path;
                const pathArr = CodeUtil.getArrBySplit(pathStrs);
                const pathBeginArea = pathArr[0];
                const type = pathData.type;
                let orderStr = OrderStr.attack;
                if (type === PathType.move) {
                    orderStr = OrderStr.move
                }

                this.registUnitPath(startRange, unit, pathBeginArea, orderStr)
                for (let i = 0; i < pathArr.length; i++) {
                    const rangeName = pathArr[i];
                    const nextIndex = i + 1;
                    if (nextIndex >= pathArr.length) {
                        break;
                    }
                    const nextRangeName = pathArr[nextIndex];
                    this.registUnitPath(rangeName, unit, nextRangeName, orderStr)
                }

                const endRect = ModelFactory.getInstance().getRectModel(pathBeginArea);
                if (endRect) {
                    const locModel = endRect.getCenterLocModel();
                    unit.issueOrderAt(orderStr, locModel)
                }
            }
            if (!CodeUtil.isNullOrZero(delay)) {
                const timerOpt = new TimeControllerOpt(undefined, delayFun, 1);
                TimeController.getInstance().startTimer({ time: delay, opt: timerOpt });
            } else {
                delayFun();
            }
        }
    }
    checkAction(unit: UnitModel, checkAction: UnitActionLevel) {
        const action = unit.data[UnitDataKey.action];
        if (action > checkAction) {
            return false;
        } else if (action < checkAction) {
            unit.data[UnitDataKey.action] = checkAction;
        }
        return true;
    }
    reflushPath() {
        const map = UnitController.pathRegist;
        map.forEach(
            (value, key) => {
                value.forEach((cache, unit) => {
                    const ret = this.checkAction(unit, UnitActionLevel.path);
                    if (ret) {
                        UnitController.getInstance().exeUnitPath(unit)
                    }
                })
            }
        )
    }
    exeUnitHatred (activeUnit: UnitModel) {
        if (activeUnit.isAliveInBattle()) {

            const strId = activeUnit.strId;
            const unitData = DataRouter.getUnitDataByUnitId(strId);
            let range: number = unitData?.hatredRange;
            if (!CodeUtil.isNullOrZero(range)) {
                let maxNum = 0;
                let targetUnit;
                ModelFactory.getInstance().mapUnitInRange(activeUnit.loc, range, (unit: UnitModel) => {
                    const unitId = unit.getHandleId();
                    const tempNum = activeUnit.data[UnitDataKey.hatredAttackedMap][unitId];
                    if (!CodeUtil.isNullOrZero(tempNum)) {
                        if (tempNum > maxNum) {
                            targetUnit = unit;
                            maxNum = tempNum;
                        }
                    }
                })
                if (targetUnit) {
                    activeUnit.issueTargetOrder(OrderStr.attack, targetUnit)
                }
            }
        }
    }
    reflushHatred() {
        const hatredMap = UnitController.hatredRegist;
        CodeUtil.mapObj(hatredMap, (activeUnitId, value) => {
            const activeUnit = ModelFactory.getInstance().getModel(activeUnitId, ModelType.unit) as UnitModel;
            const ret = this.checkAction(activeUnit, UnitActionLevel.hatred);
            if (ret) {
                UnitController.getInstance().exeUnitHatred(activeUnit)
            }
        })
    }

    clearHurtNum () {
        PlayerFactory.getInstance().mapAllPlayer(
            (player) => {
                if (player?.isValid()) {
                    if (player.data[PlayerDataKey.hurtNum] >= 0) {
                        player.data[PlayerDataKey.hurtNum] = 0;

                        const pets = player.data[PlayerDataKey.pet];
                        const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
                        for (let i = 0; i < maxPetNum; i++) {
                            const pet = pets[i];
                            if (pet) {
                                pet.data[UnitDataKey.hurtNum] = 0;
                            }
                        }
                    }
                }
            }
        )
    }

    endFightCallBack = (player: PlayerModel, unit: UnitModel, size: number) => {
        if (player?.isNotEndPlaying()) {
            if (size === 0) {
                // const tempPlayerId = GetPlayerId(player);
                // GlobalEnv.hurtNum[tempPlayerId] = 0;
                Logger.toastProduct(CodeUtil.getColorStr(ColorStr.green, '脱离战斗'), player);
            }
        } else {
            const strId = unit.strId;
            const unitData = DataRouter.getUnitDataByUnitId(strId);
            if (unitData) {
            } else {
                return;
            }
            const leaveFightReset = Configer.getConfig(ConfigKey.leaveFightReset);
            let checkLeaveFightReset = false;
            if (!CodeUtil.isNullOrZero(leaveFightReset)) {
                if (!CodeUtil.isNullOrZero(unitData.disengagement)) {
                    checkLeaveFightReset = true;
                }
            }

            if (checkLeaveFightReset) {
                const unitItem_generateData_Map = UnitController.unitItem_generateData_Map;
                const generateData = unitItem_generateData_Map.get(unit);
                if (generateData) {
                    const realLoc = generateData.realLoc;
                    if (realLoc) {
                        unit.issueOrderAt(OrderStr.move, realLoc);
                        unit.lifePercent = 1;
                        unit.manaPercent = 1;
                    }
                }
            }
        }
    }

    useTransData(units: UnitModel[], transData: TransData, bRandom: boolean = false) {
        const end = transData.tp_end;
        const uiPath = transData.UI_path;
        if (end) {
            const endRect = ModelFactory.getInstance().getRectModel(end);
            let endLoc;
            if (bRandom) {
                endLoc = endRect.getRandomLocModel();
            } else {
                endLoc = endRect.getCenterLocModel();
            }
            for (let i = 0; i < units.length; i++) {
                const unit = units[i];
                const player = PlayerFactory.getInstance().getUnitOwner(unit);
                player.moveUnit(unit, endLoc);
            }
        }
        const path = transData.music;
        if (path) {
            const musicInterval = transData.musicInterval;
            TimeController.getInstance().startBackMusic(path, musicInterval);
        }
        if (uiPath) {
            const x = transData.UI_x;
            const y = transData.UI_y;
            const h = transData.UI_h;
            const w = transData.UI_w;
            const size = { w, h }
            const pos = { x, y }
            const request: BroadcastRequest = { broadcastType: BroadcastType.showTextUi, controllerNo: ControllerNo.GuiController, data: { uiName: ResourceKey.generate, path: uiPath, size: size, pos: pos, autoClose: false } }
            ControllerBroadcast.getInstance().broadcast(request);
        }
    }
    getUnitHurts(player: PlayerModel): { unit: UnitModel, name: string, value: number, id: number }[] {
        const hurts: { unit: UnitModel, name: string, value: number, id: number }[] = [];
        const unitArr = this.getCombinationUnitArr(player);
        // const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
        for (let i = 0; i < unitArr.length; i++) {
            const unit = unitArr[i];
            const isAlive = unit?.isAlive();
            let playerName: string;
            if (unit) {
                if (isAlive) {
                    playerName = CodeUtil.getColorStr(ColorStr.green, unit.name);
                } else {
                    playerName = CodeUtil.getColorStr(ColorStr.red, unit.name);
                }
            } else {
                continue;
            }
            const value = unit.data[UnitDataKey.hurtNum];
            const hurt: { unit: UnitModel, name: string, value: number, id: number } = { unit: unit, name: playerName, value: value, id: i };
            hurts.push(hurt);
        }
        hurts.sort(
            (a, b) => {
                if (a.value > b.value) {
                    return -1
                }
                if (a.value < b.value) {
                    return 1
                }
                return 0;
            }
        )
        return hurts;
    }
    reflushMoveTime = (player: PlayerModel, interval: number) => {
        const moveTargetInfo = player.data[PlayerDataKey.moveTargetInfo];
        const moveTime = moveTargetInfo.time;
        if (moveTime >= 0) {
            moveTargetInfo.time = MathUtil.toFixed(moveTime - interval, 2);

            if (moveTime === 0) {
                player.hideMoveEffect();
            }
        }
    }
    resumeHero = (player: PlayerModel) => {
        if (player?.isNotEndPlaying()) {
            const unit = GlobalEnv.getHero(player);
            const loc = player.startLocation;
            if (unit.isValid()) {
                if (unit.isAlive()) {
                } else {
                    unit.revive(loc, true);
                    unit.data[UnitDataKey.resumeTime] = -1;
                    player.moveUnit(unit, loc);
                }
            }
        }
    }
    getUnitCost = (player: PlayerModel) => {
        const hero = GlobalEnv.getHero(player);
        const unitId = hero?.strId;
        const unitData = DataRouter.getUnitDataByUnitId(unitId);

        if (unitData) {
            const resumeNum = hero.data[UnitDataKey.resumeNum];
            const resumeBase = unitData.resumeBase ? unitData.resumeBase : 0;
            const resumeRadix = unitData.resumeRadix ? unitData.resumeRadix : 1;
            const ret = resumeBase + resumeRadix * resumeNum;
            return ret;
        }

        return 0;
    }
    addHeroResumeNum = (player: PlayerModel) => {
        const hero = GlobalEnv.getHero(player);
        if (hero) {
            hero.data[UnitDataKey.resumeNum] = hero.data[UnitDataKey.resumeNum] + 1;
        }
    }
    mapUnitSerialize = (index: number, bAll: SerializeLoadType, callBack: (key: string, index: number, type: UnitLoadType) => void) => {
        const archiveFun = (archiveIndex: number) => {
            let hiPrefix = 'hi';
            let biPrefix = 'bi';
            let piPrefix = `pi`;
            const tempHiPrefix = hiPrefix + `${archiveIndex}`;
            const tempBiPrefix = biPrefix + `${archiveIndex}`;
            const tempPiPrefix = piPrefix + `${archiveIndex}_`;
            callBack(tempHiPrefix, 0, UnitLoadType.hi);
            callBack(tempBiPrefix, 0, UnitLoadType.bi);

            // 初始化宠物
            const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
            for (let i = 0; i < maxPetNum; i++) {
                const strIndex = `${i + 1}`;
                const key = `${tempPiPrefix}${strIndex}`;
                callBack(key, i, UnitLoadType.pi);
            }
        }

        switch (bAll) {
            case SerializeLoadType.current_global:
            case SerializeLoadType.current_archive: {
                const archiveIndex = index;
                archiveFun(archiveIndex);
                break;
            }
            case SerializeLoadType.all_global:
            case SerializeLoadType.all_archive: {
                const maxArchiveNum = Configer.getConfig(ConfigKey.maxArchiveNum);
                for (let i = 0; i < maxArchiveNum; i++) {
                    const tempIndex = i + 1;
                    archiveFun(tempIndex);
                }
                break;
            }
        }
    }
}